diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 47437ba5b3..70934950e5 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; @@ -81,48 +77,6 @@ sepgsql_get_debug_audit(void) 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 * @@ -220,121 +174,6 @@ sepgsql_exec_check_perms(List *rangeTabls, bool abort) 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 * @@ -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 the client and related stuff */ + 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 2ab7a6f203..340bec6864 100644 --- a/contrib/sepgsql/label.c +++ b/contrib/sepgsql/label.c @@ -22,6 +22,7 @@ #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" @@ -34,6 +35,13 @@ #include +/* + * 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 * @@ -47,14 +55,197 @@ sepgsql_get_client_label(void) 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; } /* diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index c93da7ad70..9ce8d2d9c4 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -267,7 +267,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,