2011-01-24 09:44:48 +08:00
|
|
|
/* -------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* contrib/sepgsql/hooks.c
|
|
|
|
*
|
|
|
|
* Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
|
|
|
|
*
|
2012-01-02 07:01:58 +08:00
|
|
|
* Copyright (c) 2010-2012, PostgreSQL Global Development Group
|
2011-01-24 09:44:48 +08:00
|
|
|
*
|
|
|
|
* -------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2012-03-10 04:18:45 +08:00
|
|
|
#include "catalog/dependency.h"
|
2011-01-24 09:44:48 +08:00
|
|
|
#include "catalog/objectaccess.h"
|
|
|
|
#include "catalog/pg_class.h"
|
2011-09-24 05:09:34 +08:00
|
|
|
#include "catalog/pg_database.h"
|
2011-01-24 09:44:48 +08:00
|
|
|
#include "catalog/pg_namespace.h"
|
|
|
|
#include "catalog/pg_proc.h"
|
|
|
|
#include "commands/seclabel.h"
|
|
|
|
#include "executor/executor.h"
|
|
|
|
#include "fmgr.h"
|
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "tcop/utility.h"
|
|
|
|
#include "utils/guc.h"
|
|
|
|
|
|
|
|
#include "sepgsql.h"
|
|
|
|
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Declarations
|
|
|
|
*/
|
2011-04-10 23:42:00 +08:00
|
|
|
void _PG_init(void);
|
2011-01-24 09:44:48 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Saved hook entries (if stacked)
|
|
|
|
*/
|
2011-04-10 23:42:00 +08:00
|
|
|
static object_access_hook_type next_object_access_hook = NULL;
|
|
|
|
static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL;
|
|
|
|
static ProcessUtility_hook_type next_ProcessUtility_hook = NULL;
|
2011-12-21 22:12:43 +08:00
|
|
|
static ExecutorStart_hook_type next_ExecutorStart_hook = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Contextual information on DDL commands
|
|
|
|
*/
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
NodeTag cmdtype;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Name of the template database given by users on CREATE DATABASE
|
|
|
|
* command. Elsewhere (including the case of default) NULL.
|
|
|
|
*/
|
|
|
|
const char *createdb_dtemplate;
|
|
|
|
} sepgsql_context_info_t;
|
|
|
|
|
|
|
|
static sepgsql_context_info_t sepgsql_context_info;
|
2011-01-24 09:44:48 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* GUC: sepgsql.permissive = (on|off)
|
|
|
|
*/
|
|
|
|
static bool sepgsql_permissive;
|
|
|
|
|
|
|
|
bool
|
|
|
|
sepgsql_get_permissive(void)
|
|
|
|
{
|
|
|
|
return sepgsql_permissive;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GUC: sepgsql.debug_audit = (on|off)
|
|
|
|
*/
|
|
|
|
static bool sepgsql_debug_audit;
|
|
|
|
|
|
|
|
bool
|
|
|
|
sepgsql_get_debug_audit(void)
|
|
|
|
{
|
|
|
|
return sepgsql_debug_audit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sepgsql_object_access
|
|
|
|
*
|
|
|
|
* Entrypoint of the object_access_hook. This routine performs as
|
|
|
|
* a dispatcher of invocation based on access type and object classes.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
sepgsql_object_access(ObjectAccessType access,
|
2011-04-10 23:42:00 +08:00
|
|
|
Oid classId,
|
|
|
|
Oid objectId,
|
2012-03-10 04:18:45 +08:00
|
|
|
int subId,
|
|
|
|
void *arg)
|
2011-01-24 09:44:48 +08:00
|
|
|
{
|
|
|
|
if (next_object_access_hook)
|
2012-03-10 04:18:45 +08:00
|
|
|
(*next_object_access_hook) (access, classId, objectId, subId, arg);
|
2011-01-24 09:44:48 +08:00
|
|
|
|
|
|
|
switch (access)
|
|
|
|
{
|
|
|
|
case OAT_POST_CREATE:
|
|
|
|
switch (classId)
|
|
|
|
{
|
2011-09-24 05:09:34 +08:00
|
|
|
case DatabaseRelationId:
|
2011-12-21 22:12:43 +08:00
|
|
|
sepgsql_database_post_create(objectId,
|
|
|
|
sepgsql_context_info.createdb_dtemplate);
|
2011-09-24 05:09:34 +08:00
|
|
|
break;
|
|
|
|
|
2011-01-24 09:44:48 +08:00
|
|
|
case NamespaceRelationId:
|
|
|
|
sepgsql_schema_post_create(objectId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RelationRelationId:
|
|
|
|
if (subId == 0)
|
2011-12-21 22:12:43 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* All cases we want to apply permission checks on
|
|
|
|
* creation of a new relation are invocation of the
|
|
|
|
* heap_create_with_catalog via DefineRelation or
|
|
|
|
* OpenIntoRel.
|
|
|
|
* Elsewhere, we need neither assignment of security
|
|
|
|
* label nor permission checks.
|
|
|
|
*/
|
|
|
|
switch (sepgsql_context_info.cmdtype)
|
|
|
|
{
|
|
|
|
case T_CreateStmt:
|
|
|
|
case T_ViewStmt:
|
|
|
|
case T_CreateSeqStmt:
|
|
|
|
case T_CompositeTypeStmt:
|
|
|
|
case T_CreateForeignTableStmt:
|
|
|
|
case T_SelectStmt:
|
|
|
|
sepgsql_relation_post_create(objectId);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* via make_new_heap() */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-01-24 09:44:48 +08:00
|
|
|
else
|
|
|
|
sepgsql_attribute_post_create(objectId, subId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ProcedureRelationId:
|
|
|
|
sepgsql_proc_post_create(objectId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Ignore unsupported object classes */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-03-10 04:18:45 +08:00
|
|
|
case OAT_DROP:
|
|
|
|
{
|
|
|
|
ObjectAccessDrop *drop_arg = (ObjectAccessDrop *)arg;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No need to apply permission checks on object deletion
|
|
|
|
* due to internal cleanups; such as removal of temporary
|
|
|
|
* database object on session closed.
|
|
|
|
*/
|
|
|
|
if ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (classId)
|
|
|
|
{
|
|
|
|
case DatabaseRelationId:
|
|
|
|
sepgsql_database_drop(objectId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NamespaceRelationId:
|
|
|
|
sepgsql_schema_drop(objectId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RelationRelationId:
|
|
|
|
if (subId == 0)
|
|
|
|
sepgsql_relation_drop(objectId);
|
|
|
|
else
|
|
|
|
sepgsql_attribute_drop(objectId, subId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ProcedureRelationId:
|
|
|
|
sepgsql_proc_drop(objectId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Ignore unsupported object classes */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2011-01-24 09:44:48 +08:00
|
|
|
default:
|
2011-04-10 23:42:00 +08:00
|
|
|
elog(ERROR, "unexpected object access type: %d", (int) access);
|
2011-01-24 09:44:48 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sepgsql_exec_check_perms
|
|
|
|
*
|
|
|
|
* Entrypoint of DML permissions
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
sepgsql_exec_check_perms(List *rangeTabls, bool abort)
|
|
|
|
{
|
|
|
|
/*
|
2011-04-10 23:42:00 +08:00
|
|
|
* If security provider is stacking and one of them replied 'false' at
|
|
|
|
* least, we don't need to check any more.
|
2011-01-24 09:44:48 +08:00
|
|
|
*/
|
|
|
|
if (next_exec_check_perms_hook &&
|
2011-04-10 23:42:00 +08:00
|
|
|
!(*next_exec_check_perms_hook) (rangeTabls, abort))
|
2011-01-24 09:44:48 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!sepgsql_dml_privileges(rangeTabls, abort))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-12-21 22:12:43 +08:00
|
|
|
/*
|
|
|
|
* sepgsql_executor_start
|
|
|
|
*
|
|
|
|
* It saves contextual information during ExecutorStart to distinguish
|
|
|
|
* a case with/without permission checks later.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
sepgsql_executor_start(QueryDesc *queryDesc, int eflags)
|
|
|
|
{
|
|
|
|
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
|
|
|
|
|
|
|
|
PG_TRY();
|
|
|
|
{
|
|
|
|
if (queryDesc->operation == CMD_SELECT)
|
|
|
|
sepgsql_context_info.cmdtype = T_SelectStmt;
|
|
|
|
else if (queryDesc->operation == CMD_INSERT)
|
|
|
|
sepgsql_context_info.cmdtype = T_InsertStmt;
|
|
|
|
else if (queryDesc->operation == CMD_DELETE)
|
|
|
|
sepgsql_context_info.cmdtype = T_DeleteStmt;
|
|
|
|
else if (queryDesc->operation == CMD_UPDATE)
|
|
|
|
sepgsql_context_info.cmdtype = T_UpdateStmt;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX - If queryDesc->operation is not above four cases, an error
|
|
|
|
* shall be raised on the following executor stage soon.
|
|
|
|
*/
|
|
|
|
if (next_ExecutorStart_hook)
|
|
|
|
(*next_ExecutorStart_hook) (queryDesc, eflags);
|
|
|
|
else
|
|
|
|
standard_ExecutorStart(queryDesc, eflags);
|
|
|
|
}
|
|
|
|
PG_CATCH();
|
|
|
|
{
|
|
|
|
sepgsql_context_info = saved_context_info;
|
|
|
|
PG_RE_THROW();
|
|
|
|
}
|
|
|
|
PG_END_TRY();
|
|
|
|
sepgsql_context_info = saved_context_info;
|
|
|
|
}
|
|
|
|
|
2011-01-24 09:44:48 +08:00
|
|
|
/*
|
|
|
|
* sepgsql_utility_command
|
|
|
|
*
|
|
|
|
* It tries to rough-grained control on utility commands; some of them can
|
|
|
|
* break whole of the things if nefarious user would use.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
sepgsql_utility_command(Node *parsetree,
|
|
|
|
const char *queryString,
|
|
|
|
ParamListInfo params,
|
|
|
|
bool isTopLevel,
|
|
|
|
DestReceiver *dest,
|
|
|
|
char *completionTag)
|
|
|
|
{
|
2011-12-21 22:12:43 +08:00
|
|
|
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
|
|
|
|
ListCell *cell;
|
2011-01-24 09:44:48 +08:00
|
|
|
|
2011-12-21 22:12:43 +08:00
|
|
|
PG_TRY();
|
2011-01-24 09:44:48 +08:00
|
|
|
{
|
2011-12-21 22:12:43 +08:00
|
|
|
/*
|
|
|
|
* Check command tag to avoid nefarious operations, and save the
|
|
|
|
* current contextual information to determine whether we should
|
|
|
|
* apply permission checks here, or not.
|
|
|
|
*/
|
|
|
|
sepgsql_context_info.cmdtype = nodeTag(parsetree);
|
|
|
|
|
|
|
|
switch (nodeTag(parsetree))
|
|
|
|
{
|
|
|
|
case T_CreatedbStmt:
|
|
|
|
/*
|
|
|
|
* We hope to reference name of the source database, but it
|
|
|
|
* does not appear in system catalog. So, we save it here.
|
|
|
|
*/
|
|
|
|
foreach (cell, ((CreatedbStmt *) parsetree)->options)
|
|
|
|
{
|
|
|
|
DefElem *defel = (DefElem *) lfirst(cell);
|
|
|
|
|
|
|
|
if (strcmp(defel->defname, "template") == 0)
|
|
|
|
{
|
|
|
|
sepgsql_context_info.createdb_dtemplate
|
|
|
|
= strVal(defel->arg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_LoadStmt:
|
|
|
|
/*
|
|
|
|
* We reject LOAD command across the board on enforcing mode,
|
|
|
|
* because a binary module can arbitrarily override hooks.
|
|
|
|
*/
|
|
|
|
if (sepgsql_getenforce())
|
|
|
|
{
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg("SELinux: LOAD is not permitted")));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* Right now we don't check any other utility commands,
|
|
|
|
* because it needs more detailed information to make access
|
|
|
|
* control decision here, but we don't want to have two parse
|
|
|
|
* and analyze routines individually.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (next_ProcessUtility_hook)
|
|
|
|
(*next_ProcessUtility_hook) (parsetree, queryString, params,
|
|
|
|
isTopLevel, dest, completionTag);
|
|
|
|
else
|
|
|
|
standard_ProcessUtility(parsetree, queryString, params,
|
|
|
|
isTopLevel, dest, completionTag);
|
2011-01-24 09:44:48 +08:00
|
|
|
}
|
2011-12-21 22:12:43 +08:00
|
|
|
PG_CATCH();
|
|
|
|
{
|
|
|
|
sepgsql_context_info = saved_context_info;
|
|
|
|
PG_RE_THROW();
|
|
|
|
}
|
|
|
|
PG_END_TRY();
|
|
|
|
sepgsql_context_info = saved_context_info;
|
2011-01-24 09:44:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Module load/unload callback
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
_PG_init(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We allow to load the SE-PostgreSQL module on single-user-mode or
|
|
|
|
* shared_preload_libraries settings only.
|
|
|
|
*/
|
|
|
|
if (IsUnderPostmaster)
|
|
|
|
ereport(ERROR,
|
2011-01-24 11:47:16 +08:00
|
|
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
2011-04-10 23:42:00 +08:00
|
|
|
errmsg("sepgsql must be loaded via shared_preload_libraries")));
|
2011-01-24 09:44:48 +08:00
|
|
|
|
|
|
|
/*
|
2011-04-10 23:42:00 +08:00
|
|
|
* Check availability of SELinux on the platform. If disabled, we cannot
|
|
|
|
* activate any SE-PostgreSQL features, and we have to skip rest of
|
|
|
|
* initialization.
|
2011-01-24 09:44:48 +08:00
|
|
|
*/
|
|
|
|
if (is_selinux_enabled() < 1)
|
|
|
|
{
|
|
|
|
sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sepgsql.permissive = (on|off)
|
|
|
|
*
|
2011-04-10 23:42:00 +08:00
|
|
|
* This variable controls performing mode of SE-PostgreSQL on user's
|
|
|
|
* session.
|
2011-01-24 09:44:48 +08:00
|
|
|
*/
|
|
|
|
DefineCustomBoolVariable("sepgsql.permissive",
|
|
|
|
"Turn on/off permissive mode in SE-PostgreSQL",
|
|
|
|
NULL,
|
|
|
|
&sepgsql_permissive,
|
|
|
|
false,
|
|
|
|
PGC_SIGHUP,
|
|
|
|
GUC_NOT_IN_SAMPLE,
|
|
|
|
NULL,
|
2011-04-07 12:11:01 +08:00
|
|
|
NULL,
|
2011-01-24 09:44:48 +08:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sepgsql.debug_audit = (on|off)
|
|
|
|
*
|
2011-04-10 23:42:00 +08:00
|
|
|
* This variable allows users to turn on/off audit logs on access control
|
|
|
|
* decisions, independent from auditallow/auditdeny setting in the
|
|
|
|
* security policy. We intend to use this option for debugging purpose.
|
2011-01-24 09:44:48 +08:00
|
|
|
*/
|
|
|
|
DefineCustomBoolVariable("sepgsql.debug_audit",
|
|
|
|
"Turn on/off debug audit messages",
|
|
|
|
NULL,
|
|
|
|
&sepgsql_debug_audit,
|
|
|
|
false,
|
|
|
|
PGC_USERSET,
|
|
|
|
GUC_NOT_IN_SAMPLE,
|
|
|
|
NULL,
|
2011-04-07 12:11:01 +08:00
|
|
|
NULL,
|
2011-01-24 09:44:48 +08:00
|
|
|
NULL);
|
|
|
|
|
2011-09-01 20:37:33 +08:00
|
|
|
/* Initialize userspace access vector cache */
|
|
|
|
sepgsql_avc_init();
|
|
|
|
|
2012-02-16 02:54:26 +08:00
|
|
|
/* Initialize security label of the client and related stuff */
|
|
|
|
sepgsql_init_client_label();
|
|
|
|
|
2011-01-24 09:44:48 +08:00
|
|
|
/* Security label provider hook */
|
|
|
|
register_label_provider(SEPGSQL_LABEL_TAG,
|
|
|
|
sepgsql_object_relabel);
|
|
|
|
|
|
|
|
/* Object access hook */
|
|
|
|
next_object_access_hook = object_access_hook;
|
|
|
|
object_access_hook = sepgsql_object_access;
|
|
|
|
|
|
|
|
/* DML permission check */
|
|
|
|
next_exec_check_perms_hook = ExecutorCheckPerms_hook;
|
|
|
|
ExecutorCheckPerms_hook = sepgsql_exec_check_perms;
|
|
|
|
|
|
|
|
/* ProcessUtility hook */
|
|
|
|
next_ProcessUtility_hook = ProcessUtility_hook;
|
|
|
|
ProcessUtility_hook = sepgsql_utility_command;
|
2011-12-21 22:12:43 +08:00
|
|
|
|
|
|
|
/* ExecutorStart hook */
|
|
|
|
next_ExecutorStart_hook = ExecutorStart_hook;
|
|
|
|
ExecutorStart_hook = sepgsql_executor_start;
|
|
|
|
|
|
|
|
/* init contextual info */
|
|
|
|
memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info));
|
2011-01-24 09:44:48 +08:00
|
|
|
}
|