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';
+CREATE TABLE auth_tbl(uname text, credential text, label text);
+INSERT INTO auth_tbl
+ VALUES ('foo', 'acbd18db4cc2f85cedef654fccc4a4d8', 'sepgsql_regtest_foo_t:s0'),
+ ('var', 'b2145aac704ce76dbe1ac7adac535b23', 'sepgsql_regtest_var_t:s0'),
+ ('baz', 'b2145aac704ce76dbe1ac7adac535b23', 'sepgsql_regtest_baz_t:s0');
+SECURITY LABEL ON TABLE auth_tbl
+ IS 'system_u:object_r:sepgsql_secret_table_t:s0';
+CREATE FUNCTION auth_func(text, text) RETURNS bool
+ LANGUAGE sql
+ AS 'SELECT sepgsql_setcon(regexp_replace(sepgsql_getcon(), ''_r:.*$'', ''_r:'' || label))
+ FROM auth_tbl WHERE uname = $1 AND credential = $2';
+SECURITY LABEL ON FUNCTION auth_func(text,text)
IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
+CREATE TABLE foo_tbl(a int, b text);
+INSERT INTO foo_tbl VALUES (1, 'aaa'), (2,'bbb'), (3,'ccc'), (4,'ddd');
+SECURITY LABEL ON TABLE foo_tbl
+ IS 'system_u:object_r:sepgsql_regtest_foo_table_t:s0';
+CREATE TABLE var_tbl(x int, y text);
+INSERT INTO var_tbl VALUES (2,'xxx'), (3,'yyy'), (4,'zzz'), (5,'xyz');
+SECURITY LABEL ON TABLE var_tbl
+ IS 'system_u:object_r:sepgsql_regtest_var_table_t:s0';
--
-- Tests for default labeling behavior
--
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
(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)
+
+--
+-- Test for simulation of typical connection pooling server
+--
+SELECT sepgsql_getcon(); -- confirm client privilege
+ sepgsql_getcon
+-----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_pool_t:s0
+(1 row)
+
+-- we shouldn't allow to switch client label without trusted procedure
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:sepgsql_regtest_foo_t:s0');
+ERROR: SELinux: security policy violation
+SELECT * FROM auth_tbl; -- failed, no permission to reference
+ERROR: SELinux: security policy violation
+-- switch to "foo"
+SELECT auth_func('foo', 'acbd18db4cc2f85cedef654fccc4a4d8');
+ auth_func
+-----------
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+ sepgsql_getcon
+----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_foo_t:s0
+(1 row)
+
+SELECT * FROM foo_tbl; -- OK
+ a | b
+---+-----
+ 1 | aaa
+ 2 | bbb
+ 3 | ccc
+ 4 | ddd
+(4 rows)
+
+SELECT * FROM var_tbl; -- failed
+ERROR: SELinux: security policy violation
+SELECT * FROM auth_tbl; -- failed
+ERROR: SELinux: security policy violation
+SELECT sepgsql_setcon(NULL); -- end of session
+ sepgsql_setcon
+----------------
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+ sepgsql_getcon
+-----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_pool_t:s0
+(1 row)
+
+-- the pooler cannot touch these tables directry
+SELECT * FROM foo_tbl; -- failed
+ERROR: SELinux: security policy violation
+SELECT * FROM var_tbl; -- failed
+ERROR: SELinux: security policy violation
+-- switch to "var"
+SELECT auth_func('var', 'b2145aac704ce76dbe1ac7adac535b23');
+ auth_func
+-----------
+ t
+(1 row)
+
+SELECT sepgsql_getcon();
+ sepgsql_getcon
+----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_var_t:s0
+(1 row)
+
+SELECT * FROM foo_tbl; -- failed
+ERROR: SELinux: security policy violation
+SELECT * FROM var_tbl; -- OK
+ x | y
+---+-----
+ 2 | xxx
+ 3 | yyy
+ 4 | zzz
+ 5 | xyz
+(4 rows)
+
+SELECT * FROM auth_tbl; -- failed
+ERROR: SELinux: security policy violation
+SELECT sepgsql_setcon(NULL); -- end of session
+ sepgsql_setcon
+----------------
+ t
+(1 row)
+
+-- misc checks
+SELECT auth_func('var', 'invalid credential'); -- not works
+ auth_func
+-----------
+
+(1 row)
+
+SELECT sepgsql_getcon();
+ sepgsql_getcon
+-----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_pool_t:s0
+(1 row)
+
--
-- Clean up
--
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;
#include "access/heapam.h"
#include "access/genam.h"
+#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#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"
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;
+ }
+ }
}
/*
/*
* 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")));
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);
if (stack->new_label)
{
- client_label = stack->old_label;
+ client_label_func = stack->old_label;
stack->old_label = NULL;
}
break;
/*
* 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)
* 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")));
next_fmgr_hook = fmgr_hook;
fmgr_hook = sepgsql_fmgr_hook;
+
+ /* Transaction/Sub-transaction callbacks */
+ RegisterXactCallback(sepgsql_xact_callback, NULL);
+ RegisterSubXactCallback(sepgsql_subxact_callback, NULL);
}
/*
PG_RETURN_TEXT_P(cstring_to_text(client_label));
}
+/*
+ * 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)
*
{
"transition", SEPG_PROCESS__TRANSITION
},
+ {
+ "dyntransition", SEPG_PROCESS__DYNTRANSITION
+ },
+ {
+ "setcurrent", SEPG_PROCESS__SETCURRENT
+ },
{
NULL, 0UL
}
-policy_module(sepgsql-regtest, 1.03)
+policy_module(sepgsql-regtest, 1.04)
gen_require(`
all_userspace_class_perms
#
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
unconfined_rw_pipes(sepgsql_regtest_dba_t)
')
+# Type transition rules
+allow sepgsql_regtest_dba_t self : process { setcurrent };
+allow sepgsql_regtest_dba_t sepgsql_regtest_user_t : process { dyntransition };
+allow sepgsql_regtest_dba_t sepgsql_regtest_foo_t : process { dyntransition };
+allow sepgsql_regtest_dba_t sepgsql_regtest_var_t : process { dyntransition };
+
#
# Dummy domain for unpriv users
#
unconfined_stream_connect(sepgsql_regtest_user_t)
unconfined_rw_pipes(sepgsql_regtest_user_t)
')
+# Type transition rules
+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;
+type_transition sepgsql_regtest_user_t sepgsql_nosuch_trusted_proc_exec_t:process sepgsql_regtest_nosuch_t;
+
+#
+# Dummy domain for (virtual) connection pooler software
+#
+# XXX - this test scenario assumes sepgsql_regtest_pool_t domain performs
+# as a typical connection pool server; that switches the client label of
+# this session prior to any user queries. The sepgsql_regtest_(foo|var)_t
+# is allowed to access its own table types, but not allowed to reference
+# other's one.
+#
+role sepgsql_regtest_pool_r;
+userdom_base_user_template(sepgsql_regtest_pool)
+userdom_manage_home_role(sepgsql_regtest_pool_r, sepgsql_regtest_pool_t)
+userdom_exec_user_home_content_files(sepgsql_regtest_pool_t)
+userdom_write_user_tmp_sockets(sepgsql_regtest_pool_t)
+
+type sepgsql_regtest_foo_t;
+type sepgsql_regtest_var_t;
+type sepgsql_regtest_foo_table_t;
+type sepgsql_regtest_var_table_t;
+
+allow sepgsql_regtest_foo_t sepgsql_regtest_foo_table_t:db_table { getattr select update insert delete lock };
+allow sepgsql_regtest_foo_t sepgsql_regtest_foo_table_t:db_column { getattr select update insert };
+allow sepgsql_regtest_foo_t sepgsql_regtest_foo_table_t:db_tuple { select update insert delete };
+
+allow sepgsql_regtest_var_t sepgsql_regtest_var_table_t:db_table { getattr select update insert delete lock };
+allow sepgsql_regtest_var_t sepgsql_regtest_var_table_t:db_column { getattr select update insert };
+allow sepgsql_regtest_var_t sepgsql_regtest_var_table_t:db_tuple { select update insert delete };
+
+optional_policy(`
+ gen_require(`
+ role unconfined_r;
+ ')
+ postgresql_role(unconfined_r, sepgsql_regtest_foo_t)
+ postgresql_role(unconfined_r, sepgsql_regtest_var_t)
+ postgresql_table_object(sepgsql_regtest_foo_table_t)
+ postgresql_table_object(sepgsql_regtest_var_table_t)
+')
+optional_policy(`
+ postgresql_stream_connect(sepgsql_regtest_pool_t)
+ postgresql_role(sepgsql_regtest_pool_r, sepgsql_regtest_pool_t)
+')
+optional_policy(`
+ unconfined_stream_connect(sepgsql_regtest_pool_t)
+ unconfined_rw_pipes(sepgsql_regtest_pool_t)
+')
+# type transitions
+allow sepgsql_regtest_pool_t self:process { setcurrent };
+allow sepgsql_regtest_pool_t sepgsql_regtest_dba_t:process { transition };
+type_transition sepgsql_regtest_pool_t sepgsql_regtest_trusted_proc_exec_t:process sepgsql_regtest_dba_t;
+
+allow { sepgsql_regtest_foo_t sepgsql_regtest_var_t } self:process { setcurrent };
+allow { sepgsql_regtest_foo_t sepgsql_regtest_var_t } sepgsql_regtest_pool_t:process { dyntransition };
+
+#
+# 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
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 };
+ allow unconfined_t sepgsql_regtest_pool_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;
+
+ role unconfined_r types sepgsql_regtest_pool_t;
+ role unconfined_r types sepgsql_regtest_foo_t;
+ role unconfined_r types sepgsql_regtest_var_t;
')
#
-# Rule to check
+# Rule to execute original trusted procedures
+#
+# XXX - sepgsql_client_type contains any valid client types, so we allow
+# them to execute the original trusted procedure at once.
#
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.
- #
gen_require(`
attribute sepgsql_client_type;
')
- allow sepgsql_client_type sepgsql_regtest_trusted_proc_exec_t:db_procedure { getattr execute install };
- type_transition sepgsql_regtest_user_t sepgsql_regtest_trusted_proc_exec_t:process sepgsql_regtest_dba_t;
+ allow sepgsql_client_type { sepgsql_regtest_trusted_proc_exec_t sepgsql_nosuch_trusted_proc_exec_t }:db_procedure { getattr execute };
+
+ # 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.
+ #
+# allow sepgsql_client_type sepgsql_regtest_trusted_proc_exec_t:db_procedure { getattr execute };
+
+ # 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 };
')
* 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)
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);
--
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;
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';
+CREATE TABLE auth_tbl(uname text, credential text, label text);
+INSERT INTO auth_tbl
+ VALUES ('foo', 'acbd18db4cc2f85cedef654fccc4a4d8', 'sepgsql_regtest_foo_t:s0'),
+ ('var', 'b2145aac704ce76dbe1ac7adac535b23', 'sepgsql_regtest_var_t:s0'),
+ ('baz', 'b2145aac704ce76dbe1ac7adac535b23', 'sepgsql_regtest_baz_t:s0');
+SECURITY LABEL ON TABLE auth_tbl
+ IS 'system_u:object_r:sepgsql_secret_table_t:s0';
+
+CREATE FUNCTION auth_func(text, text) RETURNS bool
+ LANGUAGE sql
+ AS 'SELECT sepgsql_setcon(regexp_replace(sepgsql_getcon(), ''_r:.*$'', ''_r:'' || label))
+ FROM auth_tbl WHERE uname = $1 AND credential = $2';
+SECURITY LABEL ON FUNCTION auth_func(text,text)
+ IS 'system_u:object_r:sepgsql_regtest_trusted_proc_exec_t:s0';
+
+CREATE TABLE foo_tbl(a int, b text);
+INSERT INTO foo_tbl VALUES (1, 'aaa'), (2,'bbb'), (3,'ccc'), (4,'ddd');
+SECURITY LABEL ON TABLE foo_tbl
+ IS 'system_u:object_r:sepgsql_regtest_foo_table_t:s0';
+
+CREATE TABLE var_tbl(x int, y text);
+INSERT INTO var_tbl VALUES (2,'xxx'), (3,'yyy'), (4,'zzz'), (5,'xyz');
+SECURITY LABEL ON TABLE var_tbl
+ IS 'system_u:object_r:sepgsql_regtest_var_table_t:s0';
+
--
-- Tests for default labeling behavior
--
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();
+
+--
+-- Test for simulation of typical connection pooling server
+--
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_pool_t:s0
+
+-- we shouldn't allow to switch client label without trusted procedure
+SELECT sepgsql_setcon('unconfined_u:unconfined_r:sepgsql_regtest_foo_t:s0');
+
+SELECT * FROM auth_tbl; -- failed, no permission to reference
+
+-- switch to "foo"
+SELECT auth_func('foo', 'acbd18db4cc2f85cedef654fccc4a4d8');
+
+SELECT sepgsql_getcon();
+
+SELECT * FROM foo_tbl; -- OK
+
+SELECT * FROM var_tbl; -- failed
+
+SELECT * FROM auth_tbl; -- failed
+
+SELECT sepgsql_setcon(NULL); -- end of session
+SELECT sepgsql_getcon();
+
+-- the pooler cannot touch these tables directry
+SELECT * FROM foo_tbl; -- failed
+
+SELECT * FROM var_tbl; -- failed
+
+-- switch to "var"
+SELECT auth_func('var', 'b2145aac704ce76dbe1ac7adac535b23');
+
+SELECT sepgsql_getcon();
+
+SELECT * FROM foo_tbl; -- failed
+
+SELECT * FROM var_tbl; -- OK
+
+SELECT * FROM auth_tbl; -- failed
+
+SELECT sepgsql_setcon(NULL); -- end of session
+
+-- misc checks
+SELECT auth_func('var', 'invalid credential'); -- not works
+SELECT sepgsql_getcon();
+
--
-- Clean up
--
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;
$ 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>
</para>
</sect3>
+ <sect3>
+ <title>Dynamic domain transitions</title>
+ <para>
+ It is possible to use SELinux's dynamic domain transition feature
+ to switch the security label 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 should be considered carefully, because it
+ means we allows users to switch their label (also peforms a set of
+ privileges in SELinux model) in arbitrary way, unlike regular
+ mandatory way such as trusted procedures.
+ Thus, 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>
+ <para>
+ A combination of dynamic domain transition and trusted procedure
+ enables an interesting use case that fits typical process life-
+ cycle of connection pooling software.
+ Even if your connection pooling software is not allowed to run most
+ of SQL commands, it shall be available to switch the security label
+ of the client using <literal>sepgsql_setcon()</literal> function
+ to be invoked inside of the trusted procedure; that should take some
+ credential to authorize the request to switch the client label.
+ After that, this session performs with privileges of the user being
+ switched, but it shall be unavailable to reference database objects
+ labeled as other user's one.
+ Then, it can revert the security label alsp using
+ <literal>sepgsql_setcon()</literal> with <literal>NULL</literal>
+ argument, unless the security policy prevent it.
+ The points of this use case are the trusted procedure is only way
+ for the connection pooling software to switch security label of
+ the clinet, and the trusted procedure does not work without
+ appropriate credentials. In addition, it is also a point that the
+ table to store credentials is only visible from trusted procedure.
+ </para>
+ </sect3>
+
<sect3>
<title>Miscellaneous</title>
<para>
</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 of the current session to the new domain,
+ if allowed by the security policy.
+ It also accepts <literal>NULL</literal> input, and it shall be
+ considered as a transition to the original one.
+ </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.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
</sect2>
<sect2 id="sepgsql-limitations">