pgsql-v9.2-guc-sepgsql.client_label.v1.patch

application/octet-stream

Filename: pgsql-v9.2-guc-sepgsql.client_label.v1.patch
Type: application/octet-stream
Part: 0
Message: [v9.2] Add GUC sepgsql.client_label

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch v9
File+
contrib/sepgsql/expected/label.out 1 1
contrib/sepgsql/expected/misc.out 151 0
contrib/sepgsql/hooks.c 3 190
contrib/sepgsql/label.c 268 9
contrib/sepgsql/selinux.c 6 0
contrib/sepgsql/sepgsql.h 3 1
contrib/sepgsql/sepgsql-regtest.te 31 5
contrib/sepgsql/sql/label.sql 1 1
contrib/sepgsql/sql/misc.sql 67 0
 contrib/sepgsql/expected/label.out |    2 +-
 contrib/sepgsql/expected/misc.out  |  151 ++++++++++++++++++++
 contrib/sepgsql/hooks.c            |  193 +------------------------
 contrib/sepgsql/label.c            |  277 ++++++++++++++++++++++++++++++++++--
 contrib/sepgsql/selinux.c          |    6 +
 contrib/sepgsql/sepgsql-regtest.te |   36 ++++-
 contrib/sepgsql/sepgsql.h          |    4 +-
 contrib/sepgsql/sql/label.sql      |    2 +-
 contrib/sepgsql/sql/misc.sql       |   67 +++++++++
 9 files changed, 531 insertions(+), 207 deletions(-)

diff --git a/contrib/sepgsql/expected/label.out b/contrib/sepgsql/expected/label.out
index bac169f..073ad4f 100644
--- a/contrib/sepgsql/expected/label.out
+++ b/contrib/sepgsql/expected/label.out
@@ -26,7 +26,7 @@ CREATE FUNCTION f4 () RETURNS text
     AS 'SELECT sepgsql_getcon()'
     LANGUAGE sql;
 SECURITY LABEL ON FUNCTION f4()
-    IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
+    IS 'system_u:object_r:sepgsql_nosuch_trusted_proc_exec_t:s0';
 --
 -- Tests for default labeling behavior
 --
diff --git a/contrib/sepgsql/expected/misc.out b/contrib/sepgsql/expected/misc.out
index 329852c..3f1213f 100644
--- a/contrib/sepgsql/expected/misc.out
+++ b/contrib/sepgsql/expected/misc.out
@@ -3,3 +3,154 @@
 --
 LOAD '$libdir/sepgsql';		-- failed
 ERROR:  SELinux: LOAD is not permitted
+--
+-- Permission checks related to SET sepgsql.client_label
+--
+-- try to degrade client label
+CREATE OR REPLACE FUNCTION regtest_setcon_1()
+    RETURNS bool LANGUAGE SQL
+    AS 'SET sepgsql.client_label = ''unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c1,c3,c5'';
+        VALUES(true)';
+-- try to upgrade client label
+CREATE OR REPLACE FUNCTION regtest_setcon_2()
+    RETURNS bool LANGUAGE SQL
+    AS 'SET sepgsql.client_label = ''unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c255'';
+        VALUES(true)';
+-- try to reset client label
+CREATE OR REPLACE FUNCTION regtest_resetcon()
+    RETURNS bool LANGUAGE SQL
+    AS 'RESET sepgsql.client_label; VALUES(true)';
+SECURITY LABEL ON FUNCTION regtest_setcon_1()
+		 IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
+SECURITY LABEL ON FUNCTION regtest_setcon_2()
+		 IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
+SECURITY LABEL ON FUNCTION regtest_resetcon()
+		 IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
+SELECT sepgsql_getcon();	-- confirm client privilege
+                  sepgsql_getcon                   
+---------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c255
+(1 row)
+
+SET sepgsql.client_label = 'unconfined_u:unconfined_r:unconfined_t:s0:c0.c15';	-- OK
+SHOW sepgsql.client_label;
+               sepgsql.client_label               
+--------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c15
+(1 row)
+
+RESET sepgsql.client_label;		-- failed
+ERROR:  SELinux: security policy violation
+SHOW sepgsql.client_label;
+               sepgsql.client_label               
+--------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0:c0.c15
+(1 row)
+
+-- perform as sepgsql_regtest_user_t domai
+SET sepgsql.client_label = 'unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15';	-- OK
+SHOW sepgsql.client_label;
+                    sepgsql.client_label                    
+------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+(1 row)
+
+SET sepgsql.client_label = 'unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0';	-- failed
+ERROR:  SELinux: security policy violation
+SHOW sepgsql.client_label;
+                    sepgsql.client_label                    
+------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+(1 row)
+
+RESET sepgsql.client_label;		-- failed
+ERROR:  SELinux: security policy violation
+SHOW sepgsql.client_label;
+                    sepgsql.client_label                    
+------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+(1 row)
+
+SELECT regtest_setcon_1();		-- OK
+ regtest_setcon_1 
+------------------
+ t
+(1 row)
+
+SHOW sepgsql.client_label;
+                     sepgsql.client_label                     
+--------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c1,c3,c5
+(1 row)
+
+SELECT regtest_setcon_2();		-- failed
+ERROR:  SELinux: security policy violation
+CONTEXT:  SQL function "regtest_setcon_2" statement 1
+SHOW sepgsql.client_label;
+                     sepgsql.client_label                     
+--------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c1,c3,c5
+(1 row)
+
+SELECT regtest_resetcon();		-- failed
+ERROR:  SELinux: security policy violation
+CONTEXT:  SQL function "regtest_resetcon" statement 1
+SHOW sepgsql.client_label;
+                     sepgsql.client_label                     
+--------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c1,c3,c5
+(1 row)
+
+SELECT sepgsql_getcon();	-- confirm client privilege
+                       sepgsql_getcon                       
+------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+(1 row)
+
+SELECT regtest_setcon_1();		-- OK
+ regtest_setcon_1 
+------------------
+ t
+(1 row)
+
+SHOW sepgsql.client_label;
+                     sepgsql.client_label                     
+--------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c1,c3,c5
+(1 row)
+
+SELECT sepgsql_getcon();	-- confirm client privilege
+                       sepgsql_getcon                       
+------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+(1 row)
+
+SELECT regtest_setcon_2();		-- failed
+ERROR:  SELinux: security policy violation
+CONTEXT:  SQL function "regtest_setcon_2" statement 1
+SHOW sepgsql.client_label;
+                    sepgsql.client_label                    
+------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+(1 row)
+
+SELECT sepgsql_getcon();	-- confirm client privilege
+                       sepgsql_getcon                       
+------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+(1 row)
+
+RESET sepgsql.client_label;		-- failed
+ERROR:  SELinux: security policy violation
+SELECT regtest_resetcon();		-- OK
+ regtest_resetcon 
+------------------
+ t
+(1 row)
+
+SHOW sepgsql.client_label;
+                    sepgsql.client_label                    
+------------------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+(1 row)
+
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 47437ba..10a9096 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -18,7 +18,6 @@
 #include "commands/seclabel.h"
 #include "executor/executor.h"
 #include "fmgr.h"
-#include "libpq/auth.h"
 #include "miscadmin.h"
 #include "tcop/utility.h"
 #include "utils/guc.h"
@@ -36,10 +35,7 @@ void		_PG_init(void);
  * Saved hook entries (if stacked)
  */
 static object_access_hook_type next_object_access_hook = NULL;
-static ClientAuthentication_hook_type next_client_auth_hook = NULL;
 static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL;
-static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
-static fmgr_hook_type next_fmgr_hook = NULL;
 static ProcessUtility_hook_type next_ProcessUtility_hook = NULL;
 static ExecutorStart_hook_type next_ExecutorStart_hook = NULL;
 
@@ -82,48 +78,6 @@ sepgsql_get_debug_audit(void)
 }
 
 /*
- * 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)
-{
-	char	   *context;
-
-	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, &context) < 0)
-		ereport(FATAL,
-				(errcode(ERRCODE_INTERNAL_ERROR),
-				 errmsg("SELinux: unable to get peer label: %m")));
-
-	sepgsql_set_client_label(context);
-
-	/*
-	 * Switch the current performing mode from INTERNAL to either DEFAULT or
-	 * PERMISSIVE.
-	 */
-	if (sepgsql_permissive)
-		sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
-	else
-		sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
-}
-
-/*
  * sepgsql_object_access
  *
  * Entrypoint of the object_access_hook. This routine performs as
@@ -221,121 +175,6 @@ sepgsql_exec_check_perms(List *rangeTabls, bool abort)
 }
 
 /*
- * 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,
-								 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.
-				 */
-				if (stack->new_label)
-					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 = sepgsql_set_client_label(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->old_label)
-				sepgsql_set_client_label(stack->old_label);
-			stack->old_label = NULL;
-			break;
-
-		default:
-			elog(ERROR, "unexpected event type: %d", (int) event);
-			break;
-	}
-}
-
-/*
  * sepgsql_executor_start
  *
  * It saves contextual information during ExecutorStart to distinguish 
@@ -465,8 +304,6 @@ sepgsql_utility_command(Node *parsetree,
 void
 _PG_init(void)
 {
-	char	   *context;
-
 	/*
 	 * We allow to load the SE-PostgreSQL module on single-user-mode or
 	 * shared_preload_libraries settings only.
@@ -522,33 +359,16 @@ _PG_init(void)
 							 NULL,
 							 NULL);
 
-	/*
-	 * 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(&context) < 0)
-		ereport(ERROR,
-				(errcode(ERRCODE_INTERNAL_ERROR),
-				 errmsg("SELinux: failed to get server security label: %m")));
-	sepgsql_set_client_label(context);
-
 	/* Initialize userspace access vector cache */
 	sepgsql_avc_init();
 
+	/* Initialize security label of database client and related hooks */
+	sepgsql_init_client_label();
+
 	/* Security label provider hook */
 	register_label_provider(SEPGSQL_LABEL_TAG,
 							sepgsql_object_relabel);
 
-	/* Client authentication hook */
-	next_client_auth_hook = ClientAuthentication_hook;
-	ClientAuthentication_hook = sepgsql_client_auth;
-
 	/* Object access hook */
 	next_object_access_hook = object_access_hook;
 	object_access_hook = sepgsql_object_access;
@@ -557,13 +377,6 @@ _PG_init(void)
 	next_exec_check_perms_hook = ExecutorCheckPerms_hook;
 	ExecutorCheckPerms_hook = sepgsql_exec_check_perms;
 
-	/* 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;
-
 	/* ProcessUtility hook */
 	next_ProcessUtility_hook = ProcessUtility_hook;
 	ProcessUtility_hook = sepgsql_utility_command;
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index 2ab7a6f..a9ed8b8 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -22,10 +22,12 @@
 #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 "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/guc.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
 #include "utils/tqual.h"
@@ -35,26 +37,283 @@
 #include <selinux/label.h>
 
 /*
- * client_label
+ * Hooks related to security label of the database client
+ */
+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 client process
+ * security label of the database client; that can be overwritten on the case
+ * when user assigned a particular security label on sepgsql.client_label GUC,
+ * or user executed a trusted procedures.
  */
-static char *client_label = NULL;
+static char *client_label_peer = NULL;
+static char *client_label_guc  = NULL;
+static char *client_label_func = NULL;
 
 char *
 sepgsql_get_client_label(void)
 {
-	return client_label;
+	if (client_label_func)
+		return client_label_func;
+	if (client_label_guc)
+		return client_label_guc;
+	return client_label_peer;
 }
 
-char *
-sepgsql_set_client_label(char *new_label)
+/*
+ * sepgsql_assign_client_label
+ *
+ * It is a callback on assignment of sepgsql.client_label guc; to check
+ * client's capability to set this variable and permission of dynamic domain
+ * transition from current label to the new one.
+ */
+static void
+sepgsql_assign_client_label(const char *newval, void *extra)
+{
+	const char *tcontext;
+
+	/*
+	 * In case of GUC RESET, the client must be allowed to translate from
+	 * the current label to the original one being configured on client
+	 * authentication hook. If no allowed, user cannot revert his domain.
+	 */
+	if (!newval)
+		tcontext = client_label_peer;
+	else
+	{
+		if (security_check_context_raw((security_context_t) newval) < 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_NAME),
+					 errmsg("SELinux: invalid security label: \"%s\"",
+							newval)));
+		tcontext = newval;
+	}
+
+	/*
+	 * 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);
+}
+
+/*
+ * 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,
+								 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)
 {
-	char	   *old_label = client_label;
+	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.
+				 */
+				if (stack->new_label)
+					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);
 
-	client_label = new_label;
+			if (stack->new_label)
+			{
+				client_label_func = stack->old_label;
+				stack->old_label = NULL;
+			}
+			break;
 
-	return old_label;
+		default:
+			elog(ERROR, "unexpected event type: %d", (int) event);
+			break;
+	}
+}
+
+/*
+ * sepgsql_init_client_label
+ *
+ * It initializes GUC variable on security label of the database client,
+ * then set up hooks related to client authentication and trusted procedures.
+ */
+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")));
+
+	DefineCustomStringVariable("sepgsql.client_label",
+							   "security label of the database client",
+							   NULL,
+							   &client_label_guc,
+							   NULL,
+							   PGC_USERSET,
+							   GUC_NOT_IN_SAMPLE,
+							   NULL,
+							   sepgsql_assign_client_label,
+							   (GucShowHook) sepgsql_get_client_label);
+
+	/* 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;
 }
 
 /*
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 c93da7a..c97488a 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)
@@ -267,7 +269,7 @@ extern void sepgsql_avc_init(void);
  * label.c
  */
 extern char *sepgsql_get_client_label(void);
-extern char *sepgsql_set_client_label(char *new_label);
+extern void  sepgsql_init_client_label(void);
 extern char *sepgsql_get_label(Oid relOid, Oid objOid, int32 subId);
 
 extern void sepgsql_object_relabel(const ObjectAddress *object,
diff --git a/contrib/sepgsql/sql/label.sql b/contrib/sepgsql/sql/label.sql
index 2b18412..6a9df77 100644
--- a/contrib/sepgsql/sql/label.sql
+++ b/contrib/sepgsql/sql/label.sql
@@ -31,7 +31,7 @@ CREATE FUNCTION f4 () RETURNS text
     AS 'SELECT sepgsql_getcon()'
     LANGUAGE sql;
 SECURITY LABEL ON FUNCTION f4()
-    IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
+    IS 'system_u:object_r:sepgsql_nosuch_trusted_proc_exec_t:s0';
 
 --
 -- Tests for default labeling behavior
diff --git a/contrib/sepgsql/sql/misc.sql b/contrib/sepgsql/sql/misc.sql
index a46d8a6..2beae52 100644
--- a/contrib/sepgsql/sql/misc.sql
+++ b/contrib/sepgsql/sql/misc.sql
@@ -3,3 +3,70 @@
 --
 
 LOAD '$libdir/sepgsql';		-- failed
+
+
+--
+-- Permission checks related to SET sepgsql.client_label
+--
+
+-- try to degrade client label
+CREATE OR REPLACE FUNCTION regtest_setcon_1()
+    RETURNS bool LANGUAGE SQL
+    AS 'SET sepgsql.client_label = ''unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c1,c3,c5'';
+        VALUES(true)';
+-- try to upgrade client label
+CREATE OR REPLACE FUNCTION regtest_setcon_2()
+    RETURNS bool LANGUAGE SQL
+    AS 'SET sepgsql.client_label = ''unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c255'';
+        VALUES(true)';
+-- try to reset client label
+CREATE OR REPLACE FUNCTION regtest_resetcon()
+    RETURNS bool LANGUAGE SQL
+    AS 'RESET sepgsql.client_label; VALUES(true)';
+
+SECURITY LABEL ON FUNCTION regtest_setcon_1()
+		 IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
+SECURITY LABEL ON FUNCTION regtest_setcon_2()
+		 IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
+SECURITY LABEL ON FUNCTION regtest_resetcon()
+		 IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
+
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0:c0.c255
+SET sepgsql.client_label = 'unconfined_u:unconfined_r:unconfined_t:s0:c0.c15';	-- OK
+SHOW sepgsql.client_label;
+
+RESET sepgsql.client_label;		-- failed
+SHOW sepgsql.client_label;
+
+-- perform as sepgsql_regtest_user_t domain
+SET sepgsql.client_label = 'unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15';	-- OK
+SHOW sepgsql.client_label;
+
+SET sepgsql.client_label = 'unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0';	-- failed
+SHOW sepgsql.client_label;
+
+RESET sepgsql.client_label;		-- failed
+SHOW sepgsql.client_label;
+
+SELECT regtest_setcon_1();		-- OK
+SHOW sepgsql.client_label;
+
+SELECT regtest_setcon_2();		-- failed
+SHOW sepgsql.client_label;
+
+SELECT regtest_resetcon();		-- failed
+SHOW sepgsql.client_label;
+
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+SELECT regtest_setcon_1();		-- OK
+SHOW sepgsql.client_label;
+
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+SELECT regtest_setcon_2();		-- failed
+SHOW sepgsql.client_label;
+
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0:c0.c15
+RESET sepgsql.client_label;		-- failed
+
+SELECT regtest_resetcon();		-- OK
+SHOW sepgsql.client_label;