2011-01-24 02:44:48 +01:00
|
|
|
/* -------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* contrib/sepgsql/hooks.c
|
|
|
|
*
|
|
|
|
* Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
|
|
|
|
*
|
2024-01-04 02:49:05 +01:00
|
|
|
* Copyright (c) 2010-2024, PostgreSQL Global Development Group
|
2011-01-24 02:44:48 +01:00
|
|
|
*
|
|
|
|
* -------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2012-03-09 21:18:45 +01:00
|
|
|
#include "catalog/dependency.h"
|
2011-01-24 02:44:48 +01:00
|
|
|
#include "catalog/objectaccess.h"
|
|
|
|
#include "catalog/pg_class.h"
|
2011-09-23 23:09:34 +02:00
|
|
|
#include "catalog/pg_database.h"
|
2011-01-24 02:44:48 +01: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"
|
2019-10-23 05:56:22 +02:00
|
|
|
#include "sepgsql.h"
|
2011-01-24 02:44:48 +01:00
|
|
|
#include "tcop/utility.h"
|
|
|
|
#include "utils/guc.h"
|
2017-04-01 07:10:12 +02:00
|
|
|
#include "utils/queryenvironment.h"
|
2011-01-24 02:44:48 +01:00
|
|
|
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Declarations
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Saved hook entries (if stacked)
|
|
|
|
*/
|
|
|
|
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 15:12:43 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 02:44:48 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* GUC: sepgsql.permissive = (on|off)
|
|
|
|
*/
|
2022-10-31 04:44:48 +01:00
|
|
|
static bool sepgsql_permissive = false;
|
2011-01-24 02:44:48 +01:00
|
|
|
|
|
|
|
bool
|
|
|
|
sepgsql_get_permissive(void)
|
|
|
|
{
|
|
|
|
return sepgsql_permissive;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GUC: sepgsql.debug_audit = (on|off)
|
|
|
|
*/
|
2022-10-31 04:44:48 +01:00
|
|
|
static bool sepgsql_debug_audit = false;
|
2011-01-24 02:44:48 +01:00
|
|
|
|
|
|
|
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,
|
|
|
|
Oid classId,
|
|
|
|
Oid objectId,
|
2012-03-09 21:18:45 +01:00
|
|
|
int subId,
|
|
|
|
void *arg)
|
2011-01-24 02:44:48 +01:00
|
|
|
{
|
|
|
|
if (next_object_access_hook)
|
2012-03-09 21:18:45 +01:00
|
|
|
(*next_object_access_hook) (access, classId, objectId, subId, arg);
|
2011-01-24 02:44:48 +01:00
|
|
|
|
|
|
|
switch (access)
|
|
|
|
{
|
|
|
|
case OAT_POST_CREATE:
|
|
|
|
{
|
2012-10-23 23:07:26 +02:00
|
|
|
ObjectAccessPostCreate *pc_arg = arg;
|
|
|
|
bool is_internal;
|
2011-09-23 23:09:34 +02:00
|
|
|
|
2012-10-23 23:07:26 +02:00
|
|
|
is_internal = pc_arg ? pc_arg->is_internal : false;
|
2011-01-24 02:44:48 +01:00
|
|
|
|
2012-10-23 23:07:26 +02:00
|
|
|
switch (classId)
|
|
|
|
{
|
|
|
|
case DatabaseRelationId:
|
|
|
|
Assert(!is_internal);
|
|
|
|
sepgsql_database_post_create(objectId,
|
|
|
|
sepgsql_context_info.createdb_dtemplate);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NamespaceRelationId:
|
|
|
|
Assert(!is_internal);
|
|
|
|
sepgsql_schema_post_create(objectId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RelationRelationId:
|
|
|
|
if (subId == 0)
|
2011-12-21 15:12:43 +01:00
|
|
|
{
|
2012-10-23 23:07:26 +02:00
|
|
|
/*
|
|
|
|
* The cases in which we want to apply permission
|
|
|
|
* checks on creation of a new relation correspond
|
|
|
|
* to direct user invocation. For internal uses,
|
|
|
|
* that is creation of toast tables, index rebuild
|
|
|
|
* or ALTER TABLE commands, we need neither
|
|
|
|
* assignment of security labels nor permission
|
|
|
|
* checks.
|
|
|
|
*/
|
|
|
|
if (is_internal)
|
2011-12-21 15:12:43 +01:00
|
|
|
break;
|
2012-10-23 23:07:26 +02:00
|
|
|
|
|
|
|
sepgsql_relation_post_create(objectId);
|
2011-12-21 15:12:43 +01:00
|
|
|
}
|
2012-10-23 23:07:26 +02:00
|
|
|
else
|
|
|
|
sepgsql_attribute_post_create(objectId, subId);
|
|
|
|
break;
|
2011-01-24 02:44:48 +01:00
|
|
|
|
2012-10-23 23:07:26 +02:00
|
|
|
case ProcedureRelationId:
|
|
|
|
Assert(!is_internal);
|
|
|
|
sepgsql_proc_post_create(objectId);
|
|
|
|
break;
|
2011-01-24 02:44:48 +01:00
|
|
|
|
2012-10-23 23:07:26 +02:00
|
|
|
default:
|
|
|
|
/* Ignore unsupported object classes */
|
|
|
|
break;
|
|
|
|
}
|
2011-01-24 02:44:48 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-03-09 21:18:45 +01: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;
|
2013-03-27 13:10:14 +01:00
|
|
|
|
|
|
|
default:
|
|
|
|
/* Ignore unsupported object classes */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-11-23 16:41:52 +01:00
|
|
|
case OAT_TRUNCATE:
|
|
|
|
{
|
|
|
|
switch (classId)
|
|
|
|
{
|
|
|
|
case RelationRelationId:
|
|
|
|
sepgsql_relation_truncate(objectId);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Ignore unsupported object classes */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-03-27 13:10:14 +01:00
|
|
|
case OAT_POST_ALTER:
|
|
|
|
{
|
|
|
|
ObjectAccessPostAlter *pa_arg = arg;
|
|
|
|
bool is_internal = pa_arg->is_internal;
|
|
|
|
|
|
|
|
switch (classId)
|
|
|
|
{
|
|
|
|
case DatabaseRelationId:
|
|
|
|
Assert(!is_internal);
|
|
|
|
sepgsql_database_setattr(objectId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NamespaceRelationId:
|
|
|
|
Assert(!is_internal);
|
|
|
|
sepgsql_schema_setattr(objectId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RelationRelationId:
|
|
|
|
if (subId == 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* A case when we don't want to apply permission
|
|
|
|
* check is that relation is internally altered
|
|
|
|
* without user's intention. E.g, no need to check
|
|
|
|
* on toast table/index to be renamed at end of
|
|
|
|
* the table rewrites.
|
|
|
|
*/
|
|
|
|
if (is_internal)
|
|
|
|
break;
|
|
|
|
|
|
|
|
sepgsql_relation_setattr(objectId);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sepgsql_attribute_setattr(objectId, subId);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ProcedureRelationId:
|
|
|
|
Assert(!is_internal);
|
|
|
|
sepgsql_proc_setattr(objectId);
|
|
|
|
break;
|
2012-03-09 21:18:45 +01:00
|
|
|
|
|
|
|
default:
|
|
|
|
/* Ignore unsupported object classes */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-04-05 14:51:31 +02:00
|
|
|
case OAT_NAMESPACE_SEARCH:
|
|
|
|
{
|
|
|
|
ObjectAccessNamespaceSearch *ns_arg = arg;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If stacked extension already decided not to allow users to
|
|
|
|
* search this schema, we just stick with that decision.
|
|
|
|
*/
|
|
|
|
if (!ns_arg->result)
|
|
|
|
break;
|
|
|
|
|
|
|
|
Assert(classId == NamespaceRelationId);
|
|
|
|
Assert(ns_arg->result);
|
|
|
|
ns_arg->result
|
|
|
|
= sepgsql_schema_search(objectId,
|
|
|
|
ns_arg->ereport_on_violation);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-04-12 14:55:56 +02:00
|
|
|
case OAT_FUNCTION_EXECUTE:
|
|
|
|
{
|
|
|
|
Assert(classId == ProcedureRelationId);
|
|
|
|
sepgsql_proc_execute(objectId);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2011-01-24 02:44:48 +01:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unexpected object access type: %d", (int) access);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sepgsql_exec_check_perms
|
|
|
|
*
|
|
|
|
* Entrypoint of DML permissions
|
|
|
|
*/
|
|
|
|
static bool
|
2022-12-06 16:09:24 +01:00
|
|
|
sepgsql_exec_check_perms(List *rangeTbls, List *rteperminfos, bool abort)
|
2011-01-24 02:44:48 +01:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If security provider is stacking and one of them replied 'false' at
|
|
|
|
* least, we don't need to check any more.
|
|
|
|
*/
|
|
|
|
if (next_exec_check_perms_hook &&
|
2022-12-06 16:09:24 +01:00
|
|
|
!(*next_exec_check_perms_hook) (rangeTbls, rteperminfos, abort))
|
2011-01-24 02:44:48 +01:00
|
|
|
return false;
|
|
|
|
|
2022-12-06 16:09:24 +01:00
|
|
|
if (!sepgsql_dml_privileges(rangeTbls, rteperminfos, abort))
|
2011-01-24 02:44:48 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
Change representation of statement lists, and add statement location info.
This patch makes several changes that improve the consistency of
representation of lists of statements. It's always been the case
that the output of parse analysis is a list of Query nodes, whatever
the types of the individual statements in the list. This patch brings
similar consistency to the outputs of raw parsing and planning steps:
* The output of raw parsing is now always a list of RawStmt nodes;
the statement-type-dependent nodes are one level down from that.
* The output of pg_plan_queries() is now always a list of PlannedStmt
nodes, even for utility statements. In the case of a utility statement,
"planning" just consists of wrapping a CMD_UTILITY PlannedStmt around
the utility node. This list representation is now used in Portal and
CachedPlan plan lists, replacing the former convention of intermixing
PlannedStmts with bare utility-statement nodes.
Now, every list of statements has a consistent head-node type depending
on how far along it is in processing. This allows changing many places
that formerly used generic "Node *" pointers to use a more specific
pointer type, thus reducing the number of IsA() tests and casts needed,
as well as improving code clarity.
Also, the post-parse-analysis representation of DECLARE CURSOR is changed
so that it looks more like EXPLAIN, PREPARE, etc. That is, the contained
SELECT remains a child of the DeclareCursorStmt rather than getting flipped
around to be the other way. It's now true for both Query and PlannedStmt
that utilityStmt is non-null if and only if commandType is CMD_UTILITY.
That allows simplifying a lot of places that were testing both fields.
(I think some of those were just defensive programming, but in many places,
it was actually necessary to avoid confusing DECLARE CURSOR with SELECT.)
Because PlannedStmt carries a canSetTag field, we're also able to get rid
of some ad-hoc rules about how to reconstruct canSetTag for a bare utility
statement; specifically, the assumption that a utility is canSetTag if and
only if it's the only one in its list. While I see no near-term need for
relaxing that restriction, it's nice to get rid of the ad-hocery.
The API of ProcessUtility() is changed so that what it's passed is the
wrapper PlannedStmt not just the bare utility statement. This will affect
all users of ProcessUtility_hook, but the changes are pretty trivial; see
the affected contrib modules for examples of the minimum change needed.
(Most compilers should give pointer-type-mismatch warnings for uncorrected
code.)
There's also a change in the API of ExplainOneQuery_hook, to pass through
cursorOptions instead of expecting hook functions to know what to pick.
This is needed because of the DECLARE CURSOR changes, but really should
have been done in 9.6; it's unlikely that any extant hook functions
know about using CURSOR_OPT_PARALLEL_OK.
Finally, teach gram.y to save statement boundary locations in RawStmt
nodes, and pass those through to Query and PlannedStmt nodes. This allows
more intelligent handling of cases where a source query string contains
multiple statements. This patch doesn't actually do anything with the
information, but a follow-on patch will. (Passing this information through
cleanly is the true motivation for these changes; while I think this is all
good cleanup, it's unlikely we'd have bothered without this end goal.)
catversion bump because addition of location fields to struct Query
affects stored rules.
This patch is by me, but it owes a good deal to Fabien Coelho who did
a lot of preliminary work on the problem, and also reviewed the patch.
Discussion: https://postgr.es/m/alpine.DEB.2.20.1612200926310.29821@lancre
2017-01-14 22:02:35 +01:00
|
|
|
sepgsql_utility_command(PlannedStmt *pstmt,
|
2011-01-24 02:44:48 +01:00
|
|
|
const char *queryString,
|
Centralize the logic for protective copying of utility statements.
In the "simple Query" code path, it's fine for parse analysis or
execution of a utility statement to scribble on the statement's node
tree, since that'll just be thrown away afterwards. However it's
not fine if the node tree is in the plan cache, as then it'd be
corrupted for subsequent executions. Up to now we've dealt with
that by having individual utility-statement functions apply
copyObject() if they were going to modify the tree. But that's
prone to errors of omission. Bug #17053 from Charles Samborski
shows that CREATE/ALTER DOMAIN didn't get this memo, and can
crash if executed repeatedly from plan cache.
In the back branches, we'll just apply a narrow band-aid for that,
but in HEAD it seems prudent to have a more principled fix that
will close off the possibility of other similar bugs in future.
Hence, let's hoist the responsibility for doing copyObject up into
ProcessUtility from its children, thus ensuring that it happens for
all utility statement types.
Also, modify ProcessUtility's API so that its callers can tell it
whether a copy step is necessary. It turns out that in all cases,
the immediate caller knows whether the node tree is transient, so
this doesn't involve a huge amount of code thrashing. In this way,
while we lose a little bit in the execute-from-cache code path due
to sometimes copying node trees that wouldn't be mutated anyway,
we gain something in the simple-Query code path by not copying
throwaway node trees. Statements that are complex enough to be
expensive to copy are almost certainly ones that would have to be
copied anyway, so the loss in the cache code path shouldn't be much.
(Note that this whole problem applies only to utility statements.
Optimizable statements don't have the issue because we long ago made
the executor treat Plan trees as read-only. Perhaps someday we will
make utility statement execution act likewise, but I'm not holding
my breath.)
Discussion: https://postgr.es/m/931771.1623893989@sss.pgh.pa.us
Discussion: https://postgr.es/m/17053-3ca3f501bbc212b4@postgresql.org
2021-06-18 17:22:58 +02:00
|
|
|
bool readOnlyTree,
|
2013-04-28 06:18:45 +02:00
|
|
|
ProcessUtilityContext context,
|
2011-01-24 02:44:48 +01:00
|
|
|
ParamListInfo params,
|
2017-04-01 07:10:12 +02:00
|
|
|
QueryEnvironment *queryEnv,
|
2011-01-24 02:44:48 +01:00
|
|
|
DestReceiver *dest,
|
2020-03-02 22:19:51 +01:00
|
|
|
QueryCompletion *qc)
|
2011-01-24 02:44:48 +01:00
|
|
|
{
|
Change representation of statement lists, and add statement location info.
This patch makes several changes that improve the consistency of
representation of lists of statements. It's always been the case
that the output of parse analysis is a list of Query nodes, whatever
the types of the individual statements in the list. This patch brings
similar consistency to the outputs of raw parsing and planning steps:
* The output of raw parsing is now always a list of RawStmt nodes;
the statement-type-dependent nodes are one level down from that.
* The output of pg_plan_queries() is now always a list of PlannedStmt
nodes, even for utility statements. In the case of a utility statement,
"planning" just consists of wrapping a CMD_UTILITY PlannedStmt around
the utility node. This list representation is now used in Portal and
CachedPlan plan lists, replacing the former convention of intermixing
PlannedStmts with bare utility-statement nodes.
Now, every list of statements has a consistent head-node type depending
on how far along it is in processing. This allows changing many places
that formerly used generic "Node *" pointers to use a more specific
pointer type, thus reducing the number of IsA() tests and casts needed,
as well as improving code clarity.
Also, the post-parse-analysis representation of DECLARE CURSOR is changed
so that it looks more like EXPLAIN, PREPARE, etc. That is, the contained
SELECT remains a child of the DeclareCursorStmt rather than getting flipped
around to be the other way. It's now true for both Query and PlannedStmt
that utilityStmt is non-null if and only if commandType is CMD_UTILITY.
That allows simplifying a lot of places that were testing both fields.
(I think some of those were just defensive programming, but in many places,
it was actually necessary to avoid confusing DECLARE CURSOR with SELECT.)
Because PlannedStmt carries a canSetTag field, we're also able to get rid
of some ad-hoc rules about how to reconstruct canSetTag for a bare utility
statement; specifically, the assumption that a utility is canSetTag if and
only if it's the only one in its list. While I see no near-term need for
relaxing that restriction, it's nice to get rid of the ad-hocery.
The API of ProcessUtility() is changed so that what it's passed is the
wrapper PlannedStmt not just the bare utility statement. This will affect
all users of ProcessUtility_hook, but the changes are pretty trivial; see
the affected contrib modules for examples of the minimum change needed.
(Most compilers should give pointer-type-mismatch warnings for uncorrected
code.)
There's also a change in the API of ExplainOneQuery_hook, to pass through
cursorOptions instead of expecting hook functions to know what to pick.
This is needed because of the DECLARE CURSOR changes, but really should
have been done in 9.6; it's unlikely that any extant hook functions
know about using CURSOR_OPT_PARALLEL_OK.
Finally, teach gram.y to save statement boundary locations in RawStmt
nodes, and pass those through to Query and PlannedStmt nodes. This allows
more intelligent handling of cases where a source query string contains
multiple statements. This patch doesn't actually do anything with the
information, but a follow-on patch will. (Passing this information through
cleanly is the true motivation for these changes; while I think this is all
good cleanup, it's unlikely we'd have bothered without this end goal.)
catversion bump because addition of location fields to struct Query
affects stored rules.
This patch is by me, but it owes a good deal to Fabien Coelho who did
a lot of preliminary work on the problem, and also reviewed the patch.
Discussion: https://postgr.es/m/alpine.DEB.2.20.1612200926310.29821@lancre
2017-01-14 22:02:35 +01:00
|
|
|
Node *parsetree = pstmt->utilityStmt;
|
2011-12-21 15:12:43 +01:00
|
|
|
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
|
|
|
|
ListCell *cell;
|
2011-01-24 02:44:48 +01:00
|
|
|
|
2011-12-21 15:12:43 +01:00
|
|
|
PG_TRY();
|
2011-01-24 02:44:48 +01:00
|
|
|
{
|
2011-12-21 15:12:43 +01: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:
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2011-12-21 15:12:43 +01:00
|
|
|
/*
|
|
|
|
* 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:
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2011-12-21 15:12:43 +01:00
|
|
|
/*
|
|
|
|
* 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:
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2011-12-21 15:12:43 +01:00
|
|
|
/*
|
|
|
|
* 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)
|
Centralize the logic for protective copying of utility statements.
In the "simple Query" code path, it's fine for parse analysis or
execution of a utility statement to scribble on the statement's node
tree, since that'll just be thrown away afterwards. However it's
not fine if the node tree is in the plan cache, as then it'd be
corrupted for subsequent executions. Up to now we've dealt with
that by having individual utility-statement functions apply
copyObject() if they were going to modify the tree. But that's
prone to errors of omission. Bug #17053 from Charles Samborski
shows that CREATE/ALTER DOMAIN didn't get this memo, and can
crash if executed repeatedly from plan cache.
In the back branches, we'll just apply a narrow band-aid for that,
but in HEAD it seems prudent to have a more principled fix that
will close off the possibility of other similar bugs in future.
Hence, let's hoist the responsibility for doing copyObject up into
ProcessUtility from its children, thus ensuring that it happens for
all utility statement types.
Also, modify ProcessUtility's API so that its callers can tell it
whether a copy step is necessary. It turns out that in all cases,
the immediate caller knows whether the node tree is transient, so
this doesn't involve a huge amount of code thrashing. In this way,
while we lose a little bit in the execute-from-cache code path due
to sometimes copying node trees that wouldn't be mutated anyway,
we gain something in the simple-Query code path by not copying
throwaway node trees. Statements that are complex enough to be
expensive to copy are almost certainly ones that would have to be
copied anyway, so the loss in the cache code path shouldn't be much.
(Note that this whole problem applies only to utility statements.
Optimizable statements don't have the issue because we long ago made
the executor treat Plan trees as read-only. Perhaps someday we will
make utility statement execution act likewise, but I'm not holding
my breath.)
Discussion: https://postgr.es/m/931771.1623893989@sss.pgh.pa.us
Discussion: https://postgr.es/m/17053-3ca3f501bbc212b4@postgresql.org
2021-06-18 17:22:58 +02:00
|
|
|
(*next_ProcessUtility_hook) (pstmt, queryString, readOnlyTree,
|
2017-04-01 07:10:12 +02:00
|
|
|
context, params, queryEnv,
|
2020-03-02 22:19:51 +01:00
|
|
|
dest, qc);
|
2011-12-21 15:12:43 +01:00
|
|
|
else
|
Centralize the logic for protective copying of utility statements.
In the "simple Query" code path, it's fine for parse analysis or
execution of a utility statement to scribble on the statement's node
tree, since that'll just be thrown away afterwards. However it's
not fine if the node tree is in the plan cache, as then it'd be
corrupted for subsequent executions. Up to now we've dealt with
that by having individual utility-statement functions apply
copyObject() if they were going to modify the tree. But that's
prone to errors of omission. Bug #17053 from Charles Samborski
shows that CREATE/ALTER DOMAIN didn't get this memo, and can
crash if executed repeatedly from plan cache.
In the back branches, we'll just apply a narrow band-aid for that,
but in HEAD it seems prudent to have a more principled fix that
will close off the possibility of other similar bugs in future.
Hence, let's hoist the responsibility for doing copyObject up into
ProcessUtility from its children, thus ensuring that it happens for
all utility statement types.
Also, modify ProcessUtility's API so that its callers can tell it
whether a copy step is necessary. It turns out that in all cases,
the immediate caller knows whether the node tree is transient, so
this doesn't involve a huge amount of code thrashing. In this way,
while we lose a little bit in the execute-from-cache code path due
to sometimes copying node trees that wouldn't be mutated anyway,
we gain something in the simple-Query code path by not copying
throwaway node trees. Statements that are complex enough to be
expensive to copy are almost certainly ones that would have to be
copied anyway, so the loss in the cache code path shouldn't be much.
(Note that this whole problem applies only to utility statements.
Optimizable statements don't have the issue because we long ago made
the executor treat Plan trees as read-only. Perhaps someday we will
make utility statement execution act likewise, but I'm not holding
my breath.)
Discussion: https://postgr.es/m/931771.1623893989@sss.pgh.pa.us
Discussion: https://postgr.es/m/17053-3ca3f501bbc212b4@postgresql.org
2021-06-18 17:22:58 +02:00
|
|
|
standard_ProcessUtility(pstmt, queryString, readOnlyTree,
|
2017-04-01 07:10:12 +02:00
|
|
|
context, params, queryEnv,
|
2020-03-02 22:19:51 +01:00
|
|
|
dest, qc);
|
2011-01-24 02:44:48 +01:00
|
|
|
}
|
2019-11-01 11:09:52 +01:00
|
|
|
PG_FINALLY();
|
2011-12-21 15:12:43 +01:00
|
|
|
{
|
|
|
|
sepgsql_context_info = saved_context_info;
|
|
|
|
}
|
|
|
|
PG_END_TRY();
|
2011-01-24 02:44:48 +01: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 04:47:16 +01:00
|
|
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
2024-05-17 11:23:08 +02:00
|
|
|
errmsg("sepgsql must be loaded via \"shared_preload_libraries\"")));
|
2011-01-24 02:44:48 +01: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.
|
|
|
|
*/
|
|
|
|
if (is_selinux_enabled() < 1)
|
|
|
|
{
|
|
|
|
sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sepgsql.permissive = (on|off)
|
|
|
|
*
|
|
|
|
* This variable controls performing mode of SE-PostgreSQL on user's
|
|
|
|
* session.
|
|
|
|
*/
|
|
|
|
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 06:11:01 +02:00
|
|
|
NULL,
|
2011-01-24 02:44:48 +01:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sepgsql.debug_audit = (on|off)
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
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 06:11:01 +02:00
|
|
|
NULL,
|
2011-01-24 02:44:48 +01:00
|
|
|
NULL);
|
|
|
|
|
2022-02-21 20:10:15 +01:00
|
|
|
MarkGUCPrefixReserved("sepgsql");
|
2021-12-21 18:12:24 +01:00
|
|
|
|
2011-09-01 14:37:33 +02:00
|
|
|
/* Initialize userspace access vector cache */
|
|
|
|
sepgsql_avc_init();
|
|
|
|
|
2012-02-15 19:54:26 +01:00
|
|
|
/* Initialize security label of the client and related stuff */
|
|
|
|
sepgsql_init_client_label();
|
|
|
|
|
2011-01-24 02:44:48 +01: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 15:12:43 +01:00
|
|
|
|
|
|
|
/* init contextual info */
|
|
|
|
memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info));
|
2011-01-24 02:44:48 +01:00
|
|
|
}
|