diff options
Diffstat (limited to 'src/backend/parser/parse_func.c')
-rw-r--r-- | src/backend/parser/parse_func.c | 113 |
1 files changed, 112 insertions, 1 deletions
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 5de08dc78c8..8e0f42fc427 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,15 +8,18 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.224 2010/05/30 18:10:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.225 2010/07/29 23:16:33 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "catalog/pg_attrdef.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "funcapi.h" +#include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "parser/parse_agg.h" @@ -26,6 +29,7 @@ #include "parser/parse_target.h" #include "parser/parse_type.h" #include "utils/builtins.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -494,6 +498,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, retval = (Node *) wfunc; } + /* Hack to protect pg_get_expr() against misuse */ + check_pg_get_expr_args(pstate, funcid, fargs); + return retval; } @@ -1580,3 +1587,107 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) return oid; } + + +/* + * pg_get_expr() is a system function that exposes the expression + * deparsing functionality in ruleutils.c to users. Very handy, but it was + * later realized that the functions in ruleutils.c don't check the input + * rigorously, assuming it to come from system catalogs and to therefore + * be valid. That makes it easy for a user to crash the backend by passing + * a maliciously crafted string representation of an expression to + * pg_get_expr(). + * + * There's a lot of code in ruleutils.c, so it's not feasible to add + * water-proof input checking after the fact. Even if we did it once, it + * would need to be taken into account in any future patches too. + * + * Instead, we restrict pg_rule_expr() to only allow input from system + * catalogs. This is a hack, but it's the most robust and easiest + * to backpatch way of plugging the vulnerability. + * + * This is transparent to the typical usage pattern of + * "pg_get_expr(systemcolumn, ...)", but will break "pg_get_expr('foo', + * ...)", even if 'foo' is a valid expression fetched earlier from a + * system catalog. Hopefully there aren't many clients doing that out there. + */ +void +check_pg_get_expr_args(ParseState *pstate, Oid fnoid, List *args) +{ + bool allowed = false; + Node *arg; + int netlevelsup; + + /* if not being called for pg_get_expr, do nothing */ + if (fnoid != F_PG_GET_EXPR && fnoid != F_PG_GET_EXPR_EXT) + return; + + /* superusers are allowed to call it anyway (dubious) */ + if (superuser()) + return; + + /* + * The first argument must be a Var referencing one of the allowed + * system-catalog columns. It could be a join alias Var, though. + */ + Assert(list_length(args) > 1); + arg = (Node *) linitial(args); + netlevelsup = 0; + +restart: + if (IsA(arg, Var)) + { + Var *var = (Var *) arg; + RangeTblEntry *rte; + + netlevelsup += var->varlevelsup; + rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup); + + if (rte->rtekind == RTE_JOIN) + { + /* Expand join alias reference */ + if (var->varattno > 0 && + var->varattno <= list_length(rte->joinaliasvars)) + { + arg = (Node *) list_nth(rte->joinaliasvars, var->varattno - 1); + goto restart; + } + } + else if (rte->rtekind == RTE_RELATION) + { + switch (rte->relid) + { + case IndexRelationId: + if (var->varattno == Anum_pg_index_indexprs || + var->varattno == Anum_pg_index_indpred) + allowed = true; + break; + + case AttrDefaultRelationId: + if (var->varattno == Anum_pg_attrdef_adbin) + allowed = true; + break; + + case ProcedureRelationId: + if (var->varattno == Anum_pg_proc_proargdefaults) + allowed = true; + break; + + case ConstraintRelationId: + if (var->varattno == Anum_pg_constraint_conbin) + allowed = true; + break; + + case TypeRelationId: + if (var->varattno == Anum_pg_type_typdefaultbin) + allowed = true; + break; + } + } + } + + if (!allowed) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("argument to pg_get_expr() must come from system catalogs"))); +} |