sepgsql: Move some code from hooks.c to label.c

This is some preliminary refactoring related to a pending patch
to allow sepgsql-enable sessions to make dynamic label transitions.
But this commit doesn't involve any functional change: it just puts
some bits of code in more logical places.

KaiGai Kohei
This commit is contained in:
Robert Haas 2012-02-15 13:54:26 -05:00
parent 337b6f5ecf
commit d44a3fb55d
3 changed files with 200 additions and 196 deletions

View File

@ -18,7 +18,6 @@
#include "commands/seclabel.h" #include "commands/seclabel.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "fmgr.h" #include "fmgr.h"
#include "libpq/auth.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "tcop/utility.h" #include "tcop/utility.h"
#include "utils/guc.h" #include "utils/guc.h"
@ -36,10 +35,7 @@ void _PG_init(void);
* Saved hook entries (if stacked) * Saved hook entries (if stacked)
*/ */
static object_access_hook_type next_object_access_hook = NULL; 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 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 ProcessUtility_hook_type next_ProcessUtility_hook = NULL;
static ExecutorStart_hook_type next_ExecutorStart_hook = NULL; static ExecutorStart_hook_type next_ExecutorStart_hook = NULL;
@ -81,48 +77,6 @@ sepgsql_get_debug_audit(void)
return sepgsql_debug_audit; return sepgsql_debug_audit;
} }
/*
* 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 * sepgsql_object_access
* *
@ -220,121 +174,6 @@ sepgsql_exec_check_perms(List *rangeTabls, bool abort)
return true; return true;
} }
/*
* 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 * sepgsql_executor_start
* *
@ -465,8 +304,6 @@ sepgsql_utility_command(Node *parsetree,
void void
_PG_init(void) _PG_init(void)
{ {
char *context;
/* /*
* We allow to load the SE-PostgreSQL module on single-user-mode or * We allow to load the SE-PostgreSQL module on single-user-mode or
* shared_preload_libraries settings only. * shared_preload_libraries settings only.
@ -522,33 +359,16 @@ _PG_init(void)
NULL, NULL,
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 */ /* Initialize userspace access vector cache */
sepgsql_avc_init(); sepgsql_avc_init();
/* Initialize security label of the client and related stuff */
sepgsql_init_client_label();
/* Security label provider hook */ /* Security label provider hook */
register_label_provider(SEPGSQL_LABEL_TAG, register_label_provider(SEPGSQL_LABEL_TAG,
sepgsql_object_relabel); sepgsql_object_relabel);
/* Client authentication hook */
next_client_auth_hook = ClientAuthentication_hook;
ClientAuthentication_hook = sepgsql_client_auth;
/* Object access hook */ /* Object access hook */
next_object_access_hook = object_access_hook; next_object_access_hook = object_access_hook;
object_access_hook = sepgsql_object_access; object_access_hook = sepgsql_object_access;
@ -557,13 +377,6 @@ _PG_init(void)
next_exec_check_perms_hook = ExecutorCheckPerms_hook; next_exec_check_perms_hook = ExecutorCheckPerms_hook;
ExecutorCheckPerms_hook = sepgsql_exec_check_perms; 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 */ /* ProcessUtility hook */
next_ProcessUtility_hook = ProcessUtility_hook; next_ProcessUtility_hook = ProcessUtility_hook;
ProcessUtility_hook = sepgsql_utility_command; ProcessUtility_hook = sepgsql_utility_command;

View File

@ -22,6 +22,7 @@
#include "catalog/pg_proc.h" #include "catalog/pg_proc.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/seclabel.h" #include "commands/seclabel.h"
#include "libpq/auth.h"
#include "libpq/libpq-be.h" #include "libpq/libpq-be.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/builtins.h" #include "utils/builtins.h"
@ -34,6 +35,13 @@
#include <selinux/label.h> #include <selinux/label.h>
/*
* Saved hook entries (if stacked)
*/
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 * client_label
* *
@ -47,14 +55,197 @@ sepgsql_get_client_label(void)
return client_label; return client_label;
} }
char * /*
sepgsql_set_client_label(char *new_label) * 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 *old_label = client_label; if (next_client_auth_hook)
(*next_client_auth_hook) (port, status);
client_label = new_label; /*
* 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;
return old_label; /*
* Getting security label of the peer process using API of libselinux.
*/
if (getpeercon_raw(port->sock, &client_label) < 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)
{
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;
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->new_label)
{
client_label = stack->old_label;
stack->old_label = NULL;
}
break;
default:
elog(ERROR, "unexpected event type: %d", (int) event);
break;
}
}
/*
* sepgsql_init_client_label
*
* This routine initialize security label of the client, and set up related
* hooks to be invoked later.
*/
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) < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("SELinux: failed to get server security label: %m")));
/* 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;
} }
/* /*

View File

@ -267,7 +267,7 @@ extern void sepgsql_avc_init(void);
* label.c * label.c
*/ */
extern char *sepgsql_get_client_label(void); 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 char *sepgsql_get_label(Oid relOid, Oid objOid, int32 subId);
extern void sepgsql_object_relabel(const ObjectAddress *object, extern void sepgsql_object_relabel(const ObjectAddress *object,