|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * IDENTIFICATION
|
11 |
| - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.161 2003/09/29 00:05:25 petere Exp $ |
| 11 | + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.161.2.1 2010/07/30 17:57:32 tgl Exp $ |
12 | 12 | *
|
13 | 13 | *-------------------------------------------------------------------------
|
14 | 14 | */
|
15 | 15 | #include "postgres.h"
|
16 | 16 |
|
17 | 17 | #include "access/heapam.h"
|
18 | 18 | #include "catalog/catname.h"
|
| 19 | +#include "catalog/pg_attrdef.h" |
| 20 | +#include "catalog/pg_constraint.h" |
19 | 21 | #include "catalog/pg_inherits.h"
|
20 | 22 | #include "catalog/pg_proc.h"
|
21 | 23 | #include "lib/stringinfo.h"
|
| 24 | +#include "miscadmin.h" |
22 | 25 | #include "nodes/makefuncs.h"
|
| 26 | +#include "parser/parsetree.h" |
23 | 27 | #include "parser/parse_agg.h"
|
24 | 28 | #include "parser/parse_coerce.h"
|
25 | 29 | #include "parser/parse_expr.h"
|
@@ -371,6 +375,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
371 | 375 | errmsg("aggregates may not return sets")));
|
372 | 376 | }
|
373 | 377 |
|
| 378 | + /* Hack to protect pg_get_expr() against misuse */ |
| 379 | + check_pg_get_expr_args(pstate, funcid, fargs); |
| 380 | + |
374 | 381 | return retval;
|
375 | 382 | }
|
376 | 383 |
|
@@ -1531,3 +1538,119 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError)
|
1531 | 1538 |
|
1532 | 1539 | return LookupFuncName(funcname, argcount, argoids, noError);
|
1533 | 1540 | }
|
| 1541 | + |
| 1542 | + |
| 1543 | +/* |
| 1544 | + * Given an RT index and nesting depth, find the corresponding RTE. |
| 1545 | + * This is the inverse of RTERangeTablePosn. |
| 1546 | + */ |
| 1547 | +static RangeTblEntry * |
| 1548 | +GetRTEByRangeTablePosn(ParseState *pstate, |
| 1549 | + int varno, |
| 1550 | + int sublevels_up) |
| 1551 | +{ |
| 1552 | + while (sublevels_up-- > 0) |
| 1553 | + { |
| 1554 | + pstate = pstate->parentParseState; |
| 1555 | + Assert(pstate != NULL); |
| 1556 | + } |
| 1557 | + Assert(varno > 0 && varno <= length(pstate->p_rtable)); |
| 1558 | + return rt_fetch(varno, pstate->p_rtable); |
| 1559 | +} |
| 1560 | + |
| 1561 | + |
| 1562 | +/* |
| 1563 | + * pg_get_expr() is a system function that exposes the expression |
| 1564 | + * deparsing functionality in ruleutils.c to users. Very handy, but it was |
| 1565 | + * later realized that the functions in ruleutils.c don't check the input |
| 1566 | + * rigorously, assuming it to come from system catalogs and to therefore |
| 1567 | + * be valid. That makes it easy for a user to crash the backend by passing |
| 1568 | + * a maliciously crafted string representation of an expression to |
| 1569 | + * pg_get_expr(). |
| 1570 | + * |
| 1571 | + * There's a lot of code in ruleutils.c, so it's not feasible to add |
| 1572 | + * water-proof input checking after the fact. Even if we did it once, it |
| 1573 | + * would need to be taken into account in any future patches too. |
| 1574 | + * |
| 1575 | + * Instead, we restrict pg_rule_expr() to only allow input from system |
| 1576 | + * catalogs. This is a hack, but it's the most robust and easiest |
| 1577 | + * to backpatch way of plugging the vulnerability. |
| 1578 | + * |
| 1579 | + * This is transparent to the typical usage pattern of |
| 1580 | + * "pg_get_expr(systemcolumn, ...)", but will break "pg_get_expr('foo', |
| 1581 | + * ...)", even if 'foo' is a valid expression fetched earlier from a |
| 1582 | + * system catalog. Hopefully there aren't many clients doing that out there. |
| 1583 | + */ |
| 1584 | +void |
| 1585 | +check_pg_get_expr_args(ParseState *pstate, Oid fnoid, List *args) |
| 1586 | +{ |
| 1587 | + bool allowed = false; |
| 1588 | + Node *arg; |
| 1589 | + int netlevelsup; |
| 1590 | + |
| 1591 | + /* if not being called for pg_get_expr, do nothing */ |
| 1592 | + if (fnoid != F_PG_GET_EXPR && fnoid != F_PG_GET_EXPR_EXT) |
| 1593 | + return; |
| 1594 | + |
| 1595 | + /* superusers are allowed to call it anyway (dubious) */ |
| 1596 | + if (superuser()) |
| 1597 | + return; |
| 1598 | + |
| 1599 | + /* |
| 1600 | + * The first argument must be a Var referencing one of the allowed |
| 1601 | + * system-catalog columns. It could be a join alias Var, though. |
| 1602 | + */ |
| 1603 | + Assert(args != NIL); |
| 1604 | + arg = (Node *) lfirst(args); |
| 1605 | + netlevelsup = 0; |
| 1606 | + |
| 1607 | +restart: |
| 1608 | + if (IsA(arg, Var)) |
| 1609 | + { |
| 1610 | + Var *var = (Var *) arg; |
| 1611 | + RangeTblEntry *rte; |
| 1612 | + |
| 1613 | + netlevelsup += var->varlevelsup; |
| 1614 | + rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup); |
| 1615 | + |
| 1616 | + if (rte->rtekind == RTE_JOIN) |
| 1617 | + { |
| 1618 | + /* Expand join alias reference */ |
| 1619 | + if (var->varattno > 0 && |
| 1620 | + var->varattno <= length(rte->joinaliasvars)) |
| 1621 | + { |
| 1622 | + arg = (Node *) nth(var->varattno - 1, rte->joinaliasvars); |
| 1623 | + goto restart; |
| 1624 | + } |
| 1625 | + } |
| 1626 | + else if (rte->rtekind == RTE_RELATION) |
| 1627 | + { |
| 1628 | + if (rte->relid == get_system_catalog_relid(IndexRelationName)) |
| 1629 | + { |
| 1630 | + if (var->varattno == Anum_pg_index_indexprs || |
| 1631 | + var->varattno == Anum_pg_index_indpred) |
| 1632 | + allowed = true; |
| 1633 | + } |
| 1634 | + else if (rte->relid == get_system_catalog_relid(AttrDefaultRelationName)) |
| 1635 | + { |
| 1636 | + if (var->varattno == Anum_pg_attrdef_adbin) |
| 1637 | + allowed = true; |
| 1638 | + } |
| 1639 | + else if (rte->relid == get_system_catalog_relid(ConstraintRelationName)) |
| 1640 | + { |
| 1641 | + if (var->varattno == Anum_pg_constraint_conbin) |
| 1642 | + allowed = true; |
| 1643 | + } |
| 1644 | + else if (rte->relid == get_system_catalog_relid(TypeRelationName)) |
| 1645 | + { |
| 1646 | + if (var->varattno == Anum_pg_type_typdefaultbin) |
| 1647 | + allowed = true; |
| 1648 | + } |
| 1649 | + } |
| 1650 | + } |
| 1651 | + |
| 1652 | + if (!allowed) |
| 1653 | + ereport(ERROR, |
| 1654 | + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
| 1655 | + errmsg("argument to pg_get_expr() must come from system catalogs"))); |
| 1656 | +} |
0 commit comments