summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/pg_proc.c9
-rw-r--r--src/backend/commands/functioncmds.c1
-rw-r--r--src/backend/utils/fmgr/fmgr.c84
-rw-r--r--src/include/fmgr.h1
-rw-r--r--src/pl/plperl/plperl.c4
-rw-r--r--src/pl/plpgsql/src/pl_handler.c3
-rw-r--r--src/pl/plpython/plpython.c4
7 files changed, 105 insertions, 1 deletions
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 9d5f1a6cbc2..5de7b338079 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -694,6 +694,9 @@ fmgr_internal_validator(PG_FUNCTION_ARGS)
Datum tmp;
char *prosrc;
+ if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+ PG_RETURN_VOID();
+
/*
* We do not honor check_function_bodies since it's unlikely the function
* name will be found later if it isn't there now.
@@ -739,6 +742,9 @@ fmgr_c_validator(PG_FUNCTION_ARGS)
char *prosrc;
char *probin;
+ if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+ PG_RETURN_VOID();
+
/*
* It'd be most consistent to skip the check if !check_function_bodies,
* but the purpose of that switch is to be helpful for pg_dump loading,
@@ -790,6 +796,9 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
bool haspolyarg;
int i;
+ if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+ PG_RETURN_VOID();
+
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u", funcoid);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 92abd44a600..54b2054188e 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -963,7 +963,6 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
prorows);
}
-
/*
* RemoveFunction
* Deletes a function.
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 5a0e92a4fc7..0a2d4ef9565 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -24,6 +24,7 @@
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "pgstat.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgrtab.h"
#include "utils/guc.h"
@@ -2445,3 +2446,86 @@ get_call_expr_arg_stable(Node *expr, int argnum)
return false;
}
+
+/*-------------------------------------------------------------------------
+ * Support routines for procedural language implementations
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * Verify that a validator is actually associated with the language of a
+ * particular function and that the user has access to both the language and
+ * the function. All validators should call this before doing anything
+ * substantial. Doing so ensures a user cannot achieve anything with explicit
+ * calls to validators that he could not achieve with CREATE FUNCTION or by
+ * simply calling an existing function.
+ *
+ * When this function returns false, callers should skip all validation work
+ * and call PG_RETURN_VOID(). This never happens at present; it is reserved
+ * for future expansion.
+ *
+ * In particular, checking that the validator corresponds to the function's
+ * language allows untrusted language validators to assume they process only
+ * superuser-chosen source code. (Untrusted language call handlers, by
+ * definition, do assume that.) A user lacking the USAGE language privilege
+ * would be unable to reach the validator through CREATE FUNCTION, so we check
+ * that to block explicit calls as well. Checking the EXECUTE privilege on
+ * the function is often superfluous, because most users can clone the
+ * function to get an executable copy. It is meaningful against users with no
+ * database TEMP right and no permanent schema CREATE right, thereby unable to
+ * create any function. Also, if the function tracks persistent state by
+ * function OID or name, validating the original function might permit more
+ * mischief than creating and validating a clone thereof.
+ */
+bool
+CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid)
+{
+ HeapTuple procTup;
+ HeapTuple langTup;
+ Form_pg_proc procStruct;
+ Form_pg_language langStruct;
+ AclResult aclresult;
+
+ /* Get the function's pg_proc entry */
+ procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "cache lookup failed for function %u", functionOid);
+ procStruct = (Form_pg_proc) GETSTRUCT(procTup);
+
+ /*
+ * Fetch pg_language entry to know if this is the correct validation
+ * function for that pg_proc entry.
+ */
+ langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(procStruct->prolang));
+ if (!HeapTupleIsValid(langTup))
+ elog(ERROR, "cache lookup failed for language %u", procStruct->prolang);
+ langStruct = (Form_pg_language) GETSTRUCT(langTup);
+
+ if (langStruct->lanvalidator != validatorOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("language validation function %u called for language %u instead of %u",
+ validatorOid, procStruct->prolang,
+ langStruct->lanvalidator)));
+
+ /* first validate that we have permissions to use the language */
+ aclresult = pg_language_aclcheck(procStruct->prolang, GetUserId(),
+ ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_LANGUAGE,
+ NameStr(langStruct->lanname));
+
+ /*
+ * Check whether we are allowed to execute the function itself. If we can
+ * execute it, there should be no possible side-effect of
+ * compiling/validation that execution can't have.
+ */
+ aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_PROC, NameStr(procStruct->proname));
+
+ ReleaseSysCache(procTup);
+ ReleaseSysCache(langTup);
+
+ return true;
+}
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 4a412f8049c..84ef04de58b 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -624,6 +624,7 @@ extern Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);
extern Oid get_call_expr_argtype(fmNodePtr expr, int argnum);
extern bool get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum);
extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum);
+extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid);
/*
* Routines in dfmgr.c
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 896d748f6ed..7ea428e845c 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1846,6 +1846,9 @@ plperl_validator(PG_FUNCTION_ARGS)
bool istrigger = false;
int i;
+ if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+ PG_RETURN_VOID();
+
/* Get the new function's pg_proc entry */
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
if (!HeapTupleIsValid(tuple))
@@ -1925,6 +1928,7 @@ PG_FUNCTION_INFO_V1(plperlu_validator);
Datum
plperlu_validator(PG_FUNCTION_ARGS)
{
+ /* call plperl validator with our fcinfo so it gets our oid */
return plperl_validator(fcinfo);
}
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index e6e71432ff9..d5f696aaa8d 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -227,6 +227,9 @@ plpgsql_validator(PG_FUNCTION_ARGS)
bool istrigger = false;
int i;
+ if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+ PG_RETURN_VOID();
+
/* Get the new function's pg_proc entry */
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
if (!HeapTupleIsValid(tuple))
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 57801cc252e..7610031af19 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -519,6 +519,9 @@ plpython_validator(PG_FUNCTION_ARGS)
Form_pg_proc procStruct;
bool is_trigger;
+ if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+ PG_RETURN_VOID();
+
if (!check_function_bodies)
{
PG_RETURN_VOID();
@@ -5012,6 +5015,7 @@ PG_FUNCTION_INFO_V1(plpython2_validator);
Datum
plpython2_validator(PG_FUNCTION_ARGS)
{
+ /* call plpython validator with our fcinfo so it gets our oid */
return plpython_validator(fcinfo);
}