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;