diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/catalog/pg_proc.c | 9 | ||||
-rw-r--r-- | src/backend/commands/functioncmds.c | 1 | ||||
-rw-r--r-- | src/backend/utils/fmgr/fmgr.c | 84 | ||||
-rw-r--r-- | src/include/fmgr.h | 1 | ||||
-rw-r--r-- | src/pl/plperl/plperl.c | 4 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_handler.c | 3 | ||||
-rw-r--r-- | src/pl/plpython/plpy_main.c | 4 |
7 files changed, 105 insertions, 1 deletions
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 698ebafb678..abf2f497e41 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -723,6 +723,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. @@ -768,6 +771,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, @@ -819,6 +825,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 350cb76ab8c..4c8119a474d 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -1017,7 +1017,6 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) prorows); } - /* * Guts of function deletion. * diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 06409af5bb8..c7024b22257 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" @@ -2468,3 +2469,86 @@ get_fn_expr_variadic(FmgrInfo *flinfo) else 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 455c0890840..aed81cdc26b 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -629,6 +629,7 @@ 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 get_fn_expr_variadic(FmgrInfo *flinfo); +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 d9aa5efa324..ed6884e863a 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -1883,6 +1883,9 @@ plperl_validator(PG_FUNCTION_ARGS) bool is_event_trigger = 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)) @@ -1964,6 +1967,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 f02203a5fb8..f21393ae41d 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -290,6 +290,9 @@ plpgsql_validator(PG_FUNCTION_ARGS) bool is_event_trigger = 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/plpy_main.c b/src/pl/plpython/plpy_main.c index 0dad8439565..4438721589e 100644 --- a/src/pl/plpython/plpy_main.c +++ b/src/pl/plpython/plpy_main.c @@ -160,6 +160,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(); @@ -185,6 +188,7 @@ plpython_validator(PG_FUNCTION_ARGS) Datum plpython2_validator(PG_FUNCTION_ARGS) { + /* call plpython validator with our fcinfo so it gets our oid */ return plpython_validator(fcinfo); } #endif /* PY_MAJOR_VERSION < 3 */ |