|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * IDENTIFICATION
|
11 |
| - * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.175 2004/12/31 22:00:27 pgsql Exp $ |
| 11 | + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.175.4.1 2010/07/30 17:57:24 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"
|
23 | 26 | #include "parser/parse_agg.h"
|
24 | 27 | #include "parser/parse_coerce.h"
|
@@ -255,6 +258,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
255 | 258 | errmsg("aggregates may not return sets")));
|
256 | 259 | }
|
257 | 260 |
|
| 261 | + /* Hack to protect pg_get_expr() against misuse */ |
| 262 | + check_pg_get_expr_args(pstate, funcid, fargs); |
| 263 | + |
258 | 264 | return retval;
|
259 | 265 | }
|
260 | 266 |
|
@@ -1406,3 +1412,100 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError)
|
1406 | 1412 |
|
1407 | 1413 | return LookupFuncName(funcname, argcount, argoids, noError);
|
1408 | 1414 | }
|
| 1415 | + |
| 1416 | + |
| 1417 | +/* |
| 1418 | + * pg_get_expr() is a system function that exposes the expression |
| 1419 | + * deparsing functionality in ruleutils.c to users. Very handy, but it was |
| 1420 | + * later realized that the functions in ruleutils.c don't check the input |
| 1421 | + * rigorously, assuming it to come from system catalogs and to therefore |
| 1422 | + * be valid. That makes it easy for a user to crash the backend by passing |
| 1423 | + * a maliciously crafted string representation of an expression to |
| 1424 | + * pg_get_expr(). |
| 1425 | + * |
| 1426 | + * There's a lot of code in ruleutils.c, so it's not feasible to add |
| 1427 | + * water-proof input checking after the fact. Even if we did it once, it |
| 1428 | + * would need to be taken into account in any future patches too. |
| 1429 | + * |
| 1430 | + * Instead, we restrict pg_rule_expr() to only allow input from system |
| 1431 | + * catalogs. This is a hack, but it's the most robust and easiest |
| 1432 | + * to backpatch way of plugging the vulnerability. |
| 1433 | + * |
| 1434 | + * This is transparent to the typical usage pattern of |
| 1435 | + * "pg_get_expr(systemcolumn, ...)", but will break "pg_get_expr('foo', |
| 1436 | + * ...)", even if 'foo' is a valid expression fetched earlier from a |
| 1437 | + * system catalog. Hopefully there aren't many clients doing that out there. |
| 1438 | + */ |
| 1439 | +void |
| 1440 | +check_pg_get_expr_args(ParseState *pstate, Oid fnoid, List *args) |
| 1441 | +{ |
| 1442 | + bool allowed = false; |
| 1443 | + Node *arg; |
| 1444 | + int netlevelsup; |
| 1445 | + |
| 1446 | + /* if not being called for pg_get_expr, do nothing */ |
| 1447 | + if (fnoid != F_PG_GET_EXPR && fnoid != F_PG_GET_EXPR_EXT) |
| 1448 | + return; |
| 1449 | + |
| 1450 | + /* superusers are allowed to call it anyway (dubious) */ |
| 1451 | + if (superuser()) |
| 1452 | + return; |
| 1453 | + |
| 1454 | + /* |
| 1455 | + * The first argument must be a Var referencing one of the allowed |
| 1456 | + * system-catalog columns. It could be a join alias Var, though. |
| 1457 | + */ |
| 1458 | + Assert(list_length(args) > 1); |
| 1459 | + arg = (Node *) linitial(args); |
| 1460 | + netlevelsup = 0; |
| 1461 | + |
| 1462 | +restart: |
| 1463 | + if (IsA(arg, Var)) |
| 1464 | + { |
| 1465 | + Var *var = (Var *) arg; |
| 1466 | + RangeTblEntry *rte; |
| 1467 | + |
| 1468 | + netlevelsup += var->varlevelsup; |
| 1469 | + rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup); |
| 1470 | + |
| 1471 | + if (rte->rtekind == RTE_JOIN) |
| 1472 | + { |
| 1473 | + /* Expand join alias reference */ |
| 1474 | + if (var->varattno > 0 && |
| 1475 | + var->varattno <= list_length(rte->joinaliasvars)) |
| 1476 | + { |
| 1477 | + arg = (Node *) list_nth(rte->joinaliasvars, var->varattno - 1); |
| 1478 | + goto restart; |
| 1479 | + } |
| 1480 | + } |
| 1481 | + else if (rte->rtekind == RTE_RELATION) |
| 1482 | + { |
| 1483 | + if (rte->relid == get_system_catalog_relid(IndexRelationName)) |
| 1484 | + { |
| 1485 | + if (var->varattno == Anum_pg_index_indexprs || |
| 1486 | + var->varattno == Anum_pg_index_indpred) |
| 1487 | + allowed = true; |
| 1488 | + } |
| 1489 | + else if (rte->relid == get_system_catalog_relid(AttrDefaultRelationName)) |
| 1490 | + { |
| 1491 | + if (var->varattno == Anum_pg_attrdef_adbin) |
| 1492 | + allowed = true; |
| 1493 | + } |
| 1494 | + else if (rte->relid == get_system_catalog_relid(ConstraintRelationName)) |
| 1495 | + { |
| 1496 | + if (var->varattno == Anum_pg_constraint_conbin) |
| 1497 | + allowed = true; |
| 1498 | + } |
| 1499 | + else if (rte->relid == get_system_catalog_relid(TypeRelationName)) |
| 1500 | + { |
| 1501 | + if (var->varattno == Anum_pg_type_typdefaultbin) |
| 1502 | + allowed = true; |
| 1503 | + } |
| 1504 | + } |
| 1505 | + } |
| 1506 | + |
| 1507 | + if (!allowed) |
| 1508 | + ereport(ERROR, |
| 1509 | + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
| 1510 | + errmsg("argument to pg_get_expr() must come from system catalogs"))); |
| 1511 | +} |
0 commit comments