On 2012-03-10 10:39, I  wrote:

I can probably write some docs tomorrow.


Attached is v5 of the patch, with is exactly equal to v4 but with added documentation.

Some other notes.

- Robert asked why sepgsql_setcon with NULL to reset the value to the initial client label was supported. Maybe this could be a leftover from the initial implementation as GUC variable? - earlier I suggested preventing setting a new client label from a trusted procedure, however I just read in the original post that this was how the current usecase of Joshua is set up. Suggestion withdrawn.

--
Yeb Havinga
http://www.mgrid.net/
Mastering Medical Data

diff --git a/contrib/sepgsql/expected/label.out b/contrib/sepgsql/expected/label.out
index bac169f..e967c7c 100644
--- a/contrib/sepgsql/expected/label.out
+++ b/contrib/sepgsql/expected/label.out
@@ -26,6 +26,11 @@ CREATE FUNCTION f4 () RETURNS text
     AS 'SELECT sepgsql_getcon()'
     LANGUAGE sql;
 SECURITY LABEL ON FUNCTION f4()
+    IS 'system_u:object_r:sepgsql_nosuch_trusted_proc_exec_t:s0';
+CREATE FUNCTION f5 (text) RETURNS bool
+	AS 'SELECT sepgsql_setcon($1)'
+    LANGUAGE sql;
+SECURITY LABEL ON FUNCTION f5(text)
     IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
 --
 -- Tests for default labeling behavior
@@ -100,6 +105,223 @@ SELECT sepgsql_getcon();	-- client's label must be restored
 (1 row)
 
 --
+-- Test for Dynamic Domain Transition
+--
+-- validation of transaction aware dynamic-transition
+SELECT sepgsql_getcon();	-- confirm client privilege
+                  sepgsql_getcon                  
+--------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c25
+(1 row)
+
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c15');
+ sepgsql_setcon 
+----------------
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+                  sepgsql_getcon                  
+--------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c15
+(1 row)
+
+SELECT sepgsql_setcon(NULL);	-- failed to reset
+ERROR:  SELinux: security policy violation
+SELECT sepgsql_getcon();
+                  sepgsql_getcon                  
+--------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c15
+(1 row)
+
+BEGIN;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c12');
+ sepgsql_setcon 
+----------------
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+                  sepgsql_getcon                  
+--------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c12
+(1 row)
+
+SAVEPOINT svpt_1;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c9');
+ sepgsql_setcon 
+----------------
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+                 sepgsql_getcon                  
+-------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c9
+(1 row)
+
+SAVEPOINT svpt_2;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c6');
+ sepgsql_setcon 
+----------------
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+                 sepgsql_getcon                  
+-------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c6
+(1 row)
+
+SAVEPOINT svpt_3;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c3');
+ sepgsql_setcon 
+----------------
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+                 sepgsql_getcon                  
+-------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c3
+(1 row)
+
+ROLLBACK TO SAVEPOINT svpt_2;
+SELECT sepgsql_getcon();		-- should be 's0:c0.c9'
+                 sepgsql_getcon                  
+-------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c9
+(1 row)
+
+ROLLBACK TO SAVEPOINT svpt_1;
+SELECT sepgsql_getcon();		-- should be 's0:c0.c12'
+                  sepgsql_getcon                  
+--------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c12
+(1 row)
+
+ABORT;
+SELECT sepgsql_getcon();		-- should be 's0:c0.c15'
+                  sepgsql_getcon                  
+--------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c15
+(1 row)
+
+BEGIN;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c8');
+ sepgsql_setcon 
+----------------
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+                 sepgsql_getcon                  
+-------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c8
+(1 row)
+
+SAVEPOINT svpt_1;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c4');
+ sepgsql_setcon 
+----------------
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+                 sepgsql_getcon                  
+-------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c4
+(1 row)
+
+ROLLBACK TO SAVEPOINT svpt_1;
+SELECT sepgsql_getcon();		-- should be 's0:c0.c8'
+                 sepgsql_getcon                  
+-------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c8
+(1 row)
+
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c6');
+ sepgsql_setcon 
+----------------
+ t
+(1 row)
+
+COMMIT;
+SELECT sepgsql_getcon();		-- should be 's0:c0.c6'
+                 sepgsql_getcon                  
+-------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c6
+(1 row)
+
+-- sepgsql_regtest_user_t is not available dynamic-transition,
+-- unless sepgsql_setcon() is called inside of trusted-procedure
+SELECT sepgsql_getcon();	-- confirm client privilege
+                       sepgsql_getcon                       
+------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+(1 row)
+
+-- sepgsql_regtest_user_t has no permission to switch current label
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0');	-- failed
+ERROR:  SELinux: security policy violation
+SELECT sepgsql_getcon();
+                       sepgsql_getcon                       
+------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+(1 row)
+
+-- trusted procedure allows to switch, but unavailable to override MCS rules
+SELECT f5('unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c7');	-- OK
+ f5 
+----
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+                      sepgsql_getcon                       
+-----------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c7
+(1 row)
+
+SELECT f5('unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c31');	-- Failed
+ERROR:  SELinux: security policy violation
+CONTEXT:  SQL function "f5" statement 1
+SELECT sepgsql_getcon();
+                      sepgsql_getcon                       
+-----------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c7
+(1 row)
+
+SELECT f5(NULL);	-- Failed
+ERROR:  SELinux: security policy violation
+CONTEXT:  SQL function "f5" statement 1
+SELECT sepgsql_getcon();
+                      sepgsql_getcon                       
+-----------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c7
+(1 row)
+
+BEGIN;
+SELECT f5('unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c3');	-- OK
+ f5 
+----
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+                      sepgsql_getcon                       
+-----------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c3
+(1 row)
+
+ABORT;
+SELECT sepgsql_getcon();
+                      sepgsql_getcon                       
+-----------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c7
+(1 row)
+
+--
 -- Clean up
 --
 SELECT sepgsql_getcon();	-- confirm client privilege
@@ -115,3 +337,4 @@ DROP FUNCTION IF EXISTS f1() CASCADE;
 DROP FUNCTION IF EXISTS f2() CASCADE;
 DROP FUNCTION IF EXISTS f3() CASCADE;
 DROP FUNCTION IF EXISTS f4() CASCADE;
+DROP FUNCTION IF EXISTS f5(text) CASCADE;
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index 340bec6..deadd88 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -12,6 +12,7 @@
 
 #include "access/heapam.h"
 #include "access/genam.h"
+#include "access/xact.h"
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
@@ -27,7 +28,9 @@
 #include "miscadmin.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"
 #include "utils/tqual.h"
 
@@ -43,16 +46,182 @@ static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
 static fmgr_hook_type next_fmgr_hook = NULL;
 
 /*
- * client_label
+ * client_label_*
  *
- * security label of the client process
+ * 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 = NULL;
+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 refering to the client_label_*
+ * variables above.
+ */
 char *
 sepgsql_get_client_label(void)
 {
-	return client_label;
+	/* 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((security_context_t) 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.  Commmit 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;
+	ListCell   *prev;
+	ListCell   *next;
+
+	if (event == SUBXACT_EVENT_ABORT_SUB)
+	{
+		prev = NULL;
+		for (cell = list_head(client_label_pending); cell; cell = next)
+		{
+			pending_label  *plabel = lfirst(cell);
+			next = lnext(cell);
+
+			if (plabel->subid == mySubid)
+				client_label_pending
+					= list_delete_cell(client_label_pending, cell, prev);
+			else
+				prev = cell;
+		}
+	}
 }
 
 /*
@@ -78,7 +247,7 @@ sepgsql_client_auth(Port *port, int status)
 	/*
 	 * Getting security label of the peer process using API of libselinux.
 	 */
-	if (getpeercon_raw(port->sock, &client_label) < 0)
+	if (getpeercon_raw(port->sock, &client_label_peer) < 0)
 		ereport(FATAL,
 				(errcode(ERRCODE_INTERNAL_ERROR),
 				 errmsg("SELinux: unable to get peer label: %m")));
@@ -185,8 +354,8 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
 			Assert(!stack->old_label);
 			if (stack->new_label)
 			{
-				stack->old_label = client_label;
-				client_label = 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);
@@ -201,7 +370,7 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
 
 			if (stack->new_label)
 			{
-				client_label = stack->old_label;
+				client_label_func = stack->old_label;
 				stack->old_label = NULL;
 			}
 			break;
@@ -215,8 +384,8 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
 /*
  * sepgsql_init_client_label
  *
- * This routine initialize security label of the client, and set up related
- * hooks to be invoked later.
+ * Initializes the client security label and sets up related hooks for client
+ * label management.
  */
 void
 sepgsql_init_client_label(void)
@@ -231,7 +400,7 @@ sepgsql_init_client_label(void)
 	 * 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) < 0)
+	if (getcon_raw(&client_label_peer) < 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INTERNAL_ERROR),
 				 errmsg("SELinux: failed to get server security label: %m")));
@@ -246,6 +415,10 @@ sepgsql_init_client_label(void)
 
 	next_fmgr_hook = fmgr_hook;
 	fmgr_hook = sepgsql_fmgr_hook;
+
+	/* Transaction/Sub-transaction callbacks */
+	RegisterXactCallback(sepgsql_xact_callback, NULL);
+	RegisterSubXactCallback(sepgsql_subxact_callback, NULL);
 }
 
 /*
@@ -361,6 +534,27 @@ sepgsql_getcon(PG_FUNCTION_ARGS)
 }
 
 /*
+ * 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
diff --git a/contrib/sepgsql/selinux.c b/contrib/sepgsql/selinux.c
index 8819b8c..0652e29 100644
--- a/contrib/sepgsql/selinux.c
+++ b/contrib/sepgsql/selinux.c
@@ -46,6 +46,12 @@ static struct
 				"transition", SEPG_PROCESS__TRANSITION
 			},
 			{
+				"dyntransition", SEPG_PROCESS__DYNTRANSITION
+			},
+			{
+				"setcurrent", SEPG_PROCESS__SETCURRENT
+			},
+			{
 				NULL, 0UL
 			}
 		}
diff --git a/contrib/sepgsql/sepgsql-regtest.te b/contrib/sepgsql/sepgsql-regtest.te
index a8fe247..1d489cd 100644
--- a/contrib/sepgsql/sepgsql-regtest.te
+++ b/contrib/sepgsql/sepgsql-regtest.te
@@ -1,4 +1,4 @@
-policy_module(sepgsql-regtest, 1.03)
+policy_module(sepgsql-regtest, 1.04)
 
 gen_require(`
 	all_userspace_class_perms
@@ -17,6 +17,8 @@ gen_tunable(sepgsql_regression_test_mode, false)
 #
 type sepgsql_regtest_trusted_proc_exec_t;
 postgresql_procedure_object(sepgsql_regtest_trusted_proc_exec_t)
+type sepgsql_nosuch_trusted_proc_exec_t;
+postgresql_procedure_object(sepgsql_nosuch_trusted_proc_exec_t)
 
 #
 # Test domains for database administrators
@@ -53,6 +55,15 @@ optional_policy(`
 ')
 
 #
+# Dummy domain for non-exist users
+#
+role sepgsql_regtest_nosuch_r;
+userdom_base_user_template(sepgsql_regtest_nosuch)
+optional_policy(`
+    postgresql_role(sepgsql_regtest_nosuch_r, sepgsql_regtest_nosuch_t)
+')
+
+#
 # Rules to launch psql in the dummy domains
 #
 optional_policy(`
@@ -62,11 +73,13 @@ optional_policy(`
 		type sepgsql_trusted_proc_t;
 	')
 	tunable_policy(`sepgsql_regression_test_mode',`
-		allow unconfined_t sepgsql_regtest_dba_t : process { transition };
-		allow unconfined_t sepgsql_regtest_user_t : process { transition };
+		allow unconfined_t self : process { setcurrent dyntransition };
+		allow unconfined_t sepgsql_regtest_dba_t : process { transition dyntransition };
+		allow unconfined_t sepgsql_regtest_user_t : process { transition dyntransition};
 	')
 	role unconfined_r types sepgsql_regtest_dba_t;
 	role unconfined_r types sepgsql_regtest_user_t;
+	role unconfined_r types sepgsql_regtest_nosuch_t;
 	role unconfined_r types sepgsql_trusted_proc_t;
 ')
 
@@ -76,12 +89,25 @@ optional_policy(`
 optional_policy(`
 	# These rules intends sepgsql_regtest_user_t domain to translate
 	# sepgsql_regtest_dba_t on execution of procedures labeled as
-	# sepgsql_regtest_trusted_proc_exec_t, but does not allow transition
-	# permission from sepgsql_regtest_user_t to sepgsql_regtest_dba_t.
+	# sepgsql_regtest_trusted_proc_exec_t.
+	# And, also allows to translate from sepgsql_regtest_dba_t to
+	# sepgsql_regtest_user_t via SET sepgsql.client_label.
 	#
 	gen_require(`
 		attribute sepgsql_client_type;
 	')
 	allow sepgsql_client_type sepgsql_regtest_trusted_proc_exec_t:db_procedure { getattr execute install };
+	allow sepgsql_regtest_user_t sepgsql_regtest_dba_t : process { transition };
 	type_transition sepgsql_regtest_user_t sepgsql_regtest_trusted_proc_exec_t:process sepgsql_regtest_dba_t;
+
+	allow sepgsql_regtest_dba_t self : process { setcurrent };
+	allow sepgsql_regtest_dba_t sepgsql_regtest_user_t : process { dyntransition };
+
+	# These rules intends sepgsql_regtest_user_t domain to translate
+	# sepgsql_regtest_nosuch_t on execution of procedures labeled as
+	# sepgsql_nosuch_trusted_proc_exec_t, without permissions to
+	# translate to sepgsql_nosuch_trusted_proc_exec_t.
+	#
+	allow sepgsql_client_type sepgsql_nosuch_trusted_proc_exec_t:db_procedure { getattr execute install };
+	type_transition sepgsql_regtest_user_t sepgsql_nosuch_trusted_proc_exec_t:process sepgsql_regtest_nosuch_t;
 ')
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index 9ce8d2d..0eca7aa 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -57,6 +57,8 @@
  * Internally used code of access vectors
  */
 #define SEPG_PROCESS__TRANSITION			(1<<0)
+#define SEPG_PROCESS__DYNTRANSITION			(1<<1)
+#define SEPG_PROCESS__SETCURRENT			(1<<2)
 
 #define SEPG_FILE__READ						(1<<0)
 #define SEPG_FILE__WRITE					(1<<1)
@@ -274,6 +276,7 @@ extern void sepgsql_object_relabel(const ObjectAddress *object,
 					   const char *seclabel);
 
 extern Datum sepgsql_getcon(PG_FUNCTION_ARGS);
+extern Datum sepgsql_setcon(PG_FUNCTION_ARGS);
 extern Datum sepgsql_mcstrans_in(PG_FUNCTION_ARGS);
 extern Datum sepgsql_mcstrans_out(PG_FUNCTION_ARGS);
 extern Datum sepgsql_restorecon(PG_FUNCTION_ARGS);
diff --git a/contrib/sepgsql/sepgsql.sql.in b/contrib/sepgsql/sepgsql.sql.in
index 45ffe31..917d12d 100644
--- a/contrib/sepgsql/sepgsql.sql.in
+++ b/contrib/sepgsql/sepgsql.sql.in
@@ -30,6 +30,7 @@
 --
 LOAD 'MODULE_PATHNAME';
 CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_getcon() RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_getcon' LANGUAGE C;
+CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_setcon(text) RETURNS bool AS 'MODULE_PATHNAME', 'sepgsql_setcon' LANGUAGE C;
 CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_in(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_in' LANGUAGE C STRICT;
 CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_out(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_out' LANGUAGE C STRICT;
 CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_restorecon(text) RETURNS bool AS 'MODULE_PATHNAME', 'sepgsql_restorecon' LANGUAGE C;
diff --git a/contrib/sepgsql/sql/label.sql b/contrib/sepgsql/sql/label.sql
index 2b18412..24dba45 100644
--- a/contrib/sepgsql/sql/label.sql
+++ b/contrib/sepgsql/sql/label.sql
@@ -31,6 +31,12 @@ CREATE FUNCTION f4 () RETURNS text
     AS 'SELECT sepgsql_getcon()'
     LANGUAGE sql;
 SECURITY LABEL ON FUNCTION f4()
+    IS 'system_u:object_r:sepgsql_nosuch_trusted_proc_exec_t:s0';
+
+CREATE FUNCTION f5 (text) RETURNS bool
+	AS 'SELECT sepgsql_setcon($1)'
+    LANGUAGE sql;
+SECURITY LABEL ON FUNCTION f5(text)
     IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
 
 --
@@ -69,6 +75,83 @@ SELECT f4();			-- failed on domain transition
 SELECT sepgsql_getcon();	-- client's label must be restored
 
 --
+-- Test for Dynamic Domain Transition
+--
+
+-- validation of transaction aware dynamic-transition
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0:c0.c25
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c15');
+SELECT sepgsql_getcon();
+
+SELECT sepgsql_setcon(NULL);	-- failed to reset
+SELECT sepgsql_getcon();
+
+BEGIN;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c12');
+SELECT sepgsql_getcon();
+
+SAVEPOINT svpt_1;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c9');
+SELECT sepgsql_getcon();
+
+SAVEPOINT svpt_2;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c6');
+SELECT sepgsql_getcon();
+
+SAVEPOINT svpt_3;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c3');
+SELECT sepgsql_getcon();
+
+ROLLBACK TO SAVEPOINT svpt_2;
+SELECT sepgsql_getcon();		-- should be 's0:c0.c9'
+
+ROLLBACK TO SAVEPOINT svpt_1;
+SELECT sepgsql_getcon();		-- should be 's0:c0.c12'
+
+ABORT;
+SELECT sepgsql_getcon();		-- should be 's0:c0.c15'
+
+BEGIN;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c8');
+SELECT sepgsql_getcon();
+
+SAVEPOINT svpt_1;
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c4');
+SELECT sepgsql_getcon();
+
+ROLLBACK TO SAVEPOINT svpt_1;
+SELECT sepgsql_getcon();		-- should be 's0:c0.c8'
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0:c0.c6');
+
+COMMIT;
+SELECT sepgsql_getcon();		-- should be 's0:c0.c6'
+
+-- sepgsql_regtest_user_t is not available dynamic-transition,
+-- unless sepgsql_setcon() is called inside of trusted-procedure
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+
+-- sepgsql_regtest_user_t has no permission to switch current label
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0');	-- failed
+SELECT sepgsql_getcon();
+
+-- trusted procedure allows to switch, but unavailable to override MCS rules
+SELECT f5('unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c7');	-- OK
+SELECT sepgsql_getcon();
+
+SELECT f5('unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c31');	-- Failed
+SELECT sepgsql_getcon();
+
+SELECT f5(NULL);	-- Failed
+SELECT sepgsql_getcon();
+
+BEGIN;
+SELECT f5('unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c3');	-- OK
+SELECT sepgsql_getcon();
+
+ABORT;
+SELECT sepgsql_getcon();
+
+--
 -- Clean up
 --
 -- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c255
@@ -79,3 +162,4 @@ DROP FUNCTION IF EXISTS f1() CASCADE;
 DROP FUNCTION IF EXISTS f2() CASCADE;
 DROP FUNCTION IF EXISTS f3() CASCADE;
 DROP FUNCTION IF EXISTS f4() CASCADE;
+DROP FUNCTION IF EXISTS f5(text) CASCADE;
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index 68cc607..d6807cf 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -187,7 +187,7 @@ $ cd .../contrib/sepgsql
 $ make -f /usr/share/selinux/devel/Makefile
 $ sudo semodule -u sepgsql-regtest.pp
 $ sudo semodule -l | grep sepgsql
-sepgsql-regtest 1.03
+sepgsql-regtest 1.04
 </screen>
 
   <para>
@@ -512,6 +512,47 @@ postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
   </sect3>
 
   <sect3>
+    <title>Dynamic domain transitions</title>
+    <para>
+      It is possible to use SELinux's dynamic domain transition feature to switch
+      the security context of the client process, the client domain, to a new
+      context, if that is allowed by the security policy. The client domain needs
+      the 'setcurrent' permission and also 'dyntransaction' from the old to the
+      new domain.
+    </para>
+    <para>
+      Dynamic domain transitions are considered dangerous, because they violate
+      a SELinux concept called label tranquility, which means that object and
+      process labels do not change in a running system, after they are
+      initially configured. The dyntransition permission is only considered
+      safe when used to switch to a domain with a smaller set of privileges
+      than the original one, for example:
+    </para>
+
+<screen>
+regression=# select sepgsql_getcon();
+                    sepgsql_getcon
+-------------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
+(1 row)
+
+regression=# SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-s0:c1.c4');
+ sepgsql_setcon 
+----------------
+ t
+(1 row)
+
+regression=# SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-s0:c1.c1023');
+ERROR:  SELinux: security policy violation
+</screen>
+
+   <para>
+     In this example above we were allowed to switch from the larger MCS range
+     c1.c1023 to the smaller range c1.c4, but switching back was denied.
+   </para>
+  </sect3>
+
+  <sect3>
    <title>Miscellaneous</title>
    <para>
     We reject the <xref linkend="sql-load"> command across the board, because
@@ -519,6 +560,51 @@ postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
    </para>
 
   </sect3>
+</sect2>
+
+ <sect2 id="sepgsql-functions">
+  <title>Sepgsql Functions</title>
+  <para>
+   <xref linkend="sepgsql-functions-table"> shows the available functions.
+  </para>
+
+  <table id="sepgsql-functions-table">
+   <title>Sepgsql Functions</title>
+   <tgroup cols="2">
+    <tbody>
+     <row>
+      <entry><literal>sepgsql_getcon() returns text</literal></entry>
+      <entry>Returns the client domain, the current security label of the client.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>sepgsql_setcon(text) returns bool</literal></entry>
+      <entry>Switches the client domain to the new domain, if allowed by the security policy.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>sepgsql_mcstrans_in(text) returns text</literal></entry>
+      <entry>Translates the given qualifies MLS/MCS range into raw format if
+      the mcstrans daemon is running.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>sepgsql_mcstrans_out(text) returns text</literal></entry>
+      <entry>Translates the given raw MCS/MCS range into qualified format if
+      the mcstrans daemon is running.
+      </entry>
+     </row>
+     <row>
+      <entry><literal>sepgsql_restorecon(text) returns bool</literal></entry>
+      <entry>Sets up initial security labels for all objectes within the
+      current database. The argument may be NULL, or the name of a specfile to
+      be used as alternative of the system default.
+       <literal>cube(1) == '(1)'</literal>
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
  </sect2>
 
  <sect2 id="sepgsql-limitations">
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to