pgsql-v9.2-sepgsql-setcon.part-2.v2.patch
application/octet-stream
Filename: pgsql-v9.2-sepgsql-setcon.part-2.v2.patch
Type: application/octet-stream
Part: 0
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 | 223 | 0 |
| contrib/sepgsql/label.c | 203 | 9 |
| contrib/sepgsql/selinux.c | 6 | 0 |
| contrib/sepgsql/sepgsql.h | 3 | 0 |
| contrib/sepgsql/sepgsql-regtest.te | 31 | 5 |
| contrib/sepgsql/sql/label.sql | 84 | 0 |
contrib/sepgsql/expected/label.out | 223 ++++++++++++++++++++++++++++++++++++
contrib/sepgsql/label.c | 212 ++++++++++++++++++++++++++++++++--
contrib/sepgsql/selinux.c | 6 +
contrib/sepgsql/sepgsql-regtest.te | 36 +++++-
contrib/sepgsql/sepgsql.h | 3 +
contrib/sepgsql/sql/label.sql | 84 ++++++++++++++
6 files changed, 550 insertions(+), 14 deletions(-)
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..f5e6267 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,181 @@ 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. It shall be initialized as security
+ * label of the peer process using getpeercon(3); that can be overridden by
+ * the supplied label of sepgsql_setcon() or execution of trusted procedures.
*/
-static char *client_label = NULL;
+static char *client_label_peer = NULL; /* set by getpeercon(3) */
+static char *client_label_setcon = NULL; /* set by sepgsql_setcon() */
+static List *client_label_pending = NIL; /* list of pending_label */
+static char *client_label_func = NULL; /* set by trusted procedure */
+/*
+ * sepgsql_setcon() enables client to switch current security label as long
+ * as the security policy admit it. It is transaction-aware operations, so
+ * the new label shall be chained to the list of client_label_pending, then
+ * it shall be applied to "client_label_setcon" at transaction-commit time.
+ */
+typedef struct {
+ SubTransactionId subid;
+ char *label;
+} pending_label;
+
+/*
+ * sepgsql_get_client_label
+ *
+ * It returns current security label of the client. Any routins to check
+ * permissions has to use this routine, instead of references to
+ * client_label_* variables above.
+ * It can be overridden with invocation of trusted-procedure, thus, it
+ * returns "client_label_func" if not null. Elsewhere, sepgsql_setcon()
+ * might set an alternative security label using dynamic domain transition.
+ * Neither of them has no special state, the security label being initialized
+ * at database-logon time shall be returned.
+ */
char *
sepgsql_get_client_label(void)
{
- return client_label;
+ if (client_label_func)
+ return client_label_func;
+ if (client_label_pending)
+ {
+ pending_label *plabel = llast(client_label_pending);
+ if (plabel->label)
+ return plabel->label;
+ }
+ else if (client_label_setcon)
+ return client_label_setcon;
+ Assert(client_label_peer != NULL);
+ return client_label_peer;
+}
+
+/*
+ * sepgsql_set_client_label
+ *
+ * This routine tried to switch 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;
+
+ 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
+ */
+ 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.
+ * It saves an active alternative security label of the client, if exist.
+ */
+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_setcon)
+ pfree(client_label_setcon);
+
+ client_label_setcon = 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.
+ * It shall release an alternative security label set within sub-transaction
+ * being 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 +246,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 +353,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 +369,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;
@@ -231,7 +399,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 +414,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 +533,28 @@ sepgsql_getcon(PG_FUNCTION_ARGS)
}
/*
+ * BOOL sepgsql_setcon(TEXT)
+ *
+ * It switches the security label of the client, and returns previous
+ * security label.
+ */
+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/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;