1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-26 12:21:12 +03:00

Further fixes to the pg_get_expr() security fix in back branches.

It now emerges that the JDBC driver expects to be able to use pg_get_expr()
on an output of a sub-SELECT.  So extend the check logic to be able to recurse
into a sub-SELECT to see if the argument is ultimately coming from an
appropriate column.  Per report from Thomas Kellerer.
This commit is contained in:
Tom Lane
2010-09-25 15:57:05 -04:00
parent 5efa1444e6
commit 3c2da80df6

View File

@ -30,6 +30,7 @@
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "parser/parse_target.h" #include "parser/parse_target.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "parser/parsetree.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/fmgroids.h" #include "utils/fmgroids.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
@ -39,6 +40,7 @@
static Node *ParseComplexProjection(ParseState *pstate, char *funcname, static Node *ParseComplexProjection(ParseState *pstate, char *funcname,
Node *first_arg); Node *first_arg);
static void unknown_attribute(ParseState *pstate, Node *relref, char *attname); static void unknown_attribute(ParseState *pstate, Node *relref, char *attname);
static bool check_pg_get_expr_arg(ParseState *pstate, Node *arg, int netlevelsup);
/* /*
@ -1265,9 +1267,7 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError)
void void
check_pg_get_expr_args(ParseState *pstate, Oid fnoid, List *args) check_pg_get_expr_args(ParseState *pstate, Oid fnoid, List *args)
{ {
bool allowed = false;
Node *arg; Node *arg;
int netlevelsup;
/* if not being called for pg_get_expr, do nothing */ /* if not being called for pg_get_expr, do nothing */
if (fnoid != F_PG_GET_EXPR && fnoid != F_PG_GET_EXPR_EXT) if (fnoid != F_PG_GET_EXPR && fnoid != F_PG_GET_EXPR_EXT)
@ -1279,61 +1279,93 @@ check_pg_get_expr_args(ParseState *pstate, Oid fnoid, List *args)
/* /*
* The first argument must be a Var referencing one of the allowed * The first argument must be a Var referencing one of the allowed
* system-catalog columns. It could be a join alias Var, though. * system-catalog columns. It could be a join alias Var or subquery
* reference Var, though, so we need a recursive subroutine to chase
* through those possibilities.
*/ */
Assert(list_length(args) > 1); Assert(list_length(args) > 1);
arg = (Node *) linitial(args); arg = (Node *) linitial(args);
netlevelsup = 0;
restart: if (!check_pg_get_expr_arg(pstate, arg, 0))
if (IsA(arg, Var)) ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("argument to pg_get_expr() must come from system catalogs")));
}
static bool
check_pg_get_expr_arg(ParseState *pstate, Node *arg, int netlevelsup)
{
if (arg && IsA(arg, Var))
{ {
Var *var = (Var *) arg; Var *var = (Var *) arg;
RangeTblEntry *rte; RangeTblEntry *rte;
AttrNumber attnum;
netlevelsup += var->varlevelsup; netlevelsup += var->varlevelsup;
rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup); rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup);
attnum = var->varattno;
if (rte->rtekind == RTE_JOIN) if (rte->rtekind == RTE_JOIN)
{ {
/* Expand join alias reference */ /* Recursively examine join alias variable */
if (var->varattno > 0 && if (attnum > 0 &&
var->varattno <= list_length(rte->joinaliasvars)) attnum <= list_length(rte->joinaliasvars))
{ {
arg = (Node *) list_nth(rte->joinaliasvars, var->varattno - 1); arg = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
goto restart; return check_pg_get_expr_arg(pstate, arg, netlevelsup);
} }
} }
else if (rte->rtekind == RTE_SUBQUERY)
{
/* Subselect-in-FROM: examine sub-select's output expr */
TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
attnum);
ParseState mypstate;
if (ste == NULL || ste->resjunk)
elog(ERROR, "subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
arg = (Node *) ste->expr;
/*
* Recurse into the sub-select to see what its expr refers to.
* We have to build an additional level of ParseState to keep in
* step with varlevelsup in the subselect.
*/
MemSet(&mypstate, 0, sizeof(mypstate));
mypstate.parentParseState = pstate;
mypstate.p_rtable = rte->subquery->rtable;
/* don't bother filling the rest of the fake pstate */
return check_pg_get_expr_arg(&mypstate, arg, 0);
}
else if (rte->rtekind == RTE_RELATION) else if (rte->rtekind == RTE_RELATION)
{ {
switch (rte->relid) switch (rte->relid)
{ {
case IndexRelationId: case IndexRelationId:
if (var->varattno == Anum_pg_index_indexprs || if (attnum == Anum_pg_index_indexprs ||
var->varattno == Anum_pg_index_indpred) attnum == Anum_pg_index_indpred)
allowed = true; return true;
break; break;
case AttrDefaultRelationId: case AttrDefaultRelationId:
if (var->varattno == Anum_pg_attrdef_adbin) if (attnum == Anum_pg_attrdef_adbin)
allowed = true; return true;
break; break;
case ConstraintRelationId: case ConstraintRelationId:
if (var->varattno == Anum_pg_constraint_conbin) if (attnum == Anum_pg_constraint_conbin)
allowed = true; return true;
break; break;
case TypeRelationId: case TypeRelationId:
if (var->varattno == Anum_pg_type_typdefaultbin) if (attnum == Anum_pg_type_typdefaultbin)
allowed = true; return true;
break; break;
} }
} }
} }
if (!allowed) return false;
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("argument to pg_get_expr() must come from system catalogs")));
} }