Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > d3c4bfd951c25dab3d8c83571c73f957 > files > 21

postgresql-8.1.23-10.el5_10.src.rpm

Backport fix for CVE-2014-0061 from REL8_4_STABLE.

Author:     Noah Misch <noah@leadboat.com>

    Document security implications of check_function_bodies.

Author:     Noah Misch <noah@leadboat.com>

    Prevent privilege escalation in explicit calls to PL validators.

    The primary role of PL validators is to be called implicitly during
    CREATE FUNCTION, but they are also normal functions that a user can call
    explicitly.  Add a permissions check to each validator to ensure that a
    user cannot use explicit validator calls to achieve things he could not
    otherwise achieve.  Back-patch to 8.4 (all supported versions).
    Non-core procedural language extensions ought to make the same two-line
    change to their own validators.

    Andres Freund, reviewed by Tom Lane and Noah Misch.


diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index ecee550..42c802a 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -3215,9 +3215,11 @@ SELECT * FROM parent WHERE key = 2400;
         This parameter is normally on. When set to <literal>off</>, it
         disables validation of the function body string during <xref
         linkend="sql-createfunction"
-        endterm="sql-createfunction-title">. Disabling validation is
-        occasionally useful to avoid problems such as forward references
-        when restoring function definitions from a dump.
+        endterm="sql-createfunction-title">.  Disabling validation avoids side
+        effects of the validation process and avoids false positives due
+        to problems such as forward references.  Set this parameter
+        to <literal>off</> before loading functions on behalf of other
+        users; <application>pg_dump</> does so automatically.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index f1a75de..fc7494b 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -425,6 +425,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.
@@ -474,6 +477,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,
@@ -525,6 +531,9 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
 	bool		haspolyarg;
 	int			i;
 
+	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+		PG_RETURN_VOID();
+
 	tuple = SearchSysCache(PROCOID,
 						   ObjectIdGetDatum(funcoid),
 						   0, 0, 0);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 13e4b45..d26f7c7 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -669,7 +669,6 @@ CreateFunction(CreateFunctionStmt *stmt)
 					PointerGetDatum(parameterNames));
 }
 
-
 /*
  * RemoveFunction
  *		Deletes a function.
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 1c85102..eb1c42a 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -21,6 +21,7 @@
 #include "executor/functions.h"
 #include "miscadmin.h"
 #include "parser/parse_expr.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgrtab.h"
 #include "utils/lsyscache.h"
@@ -1924,3 +1925,87 @@ get_call_expr_argtype(Node *expr, int argnum)
 
 	return argtype;
 }
+
+/*-------------------------------------------------------------------------
+ *		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 = SearchSysCache(PROCOID, ObjectIdGetDatum(functionOid), 0, 0, 0);
+	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 = SearchSysCache(LANGOID, ObjectIdGetDatum(procStruct->prolang),
+							 0, 0, 0);
+	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 b7a85f1..ccc6b0e 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -401,6 +401,7 @@ extern Oid	fmgr_internal_function(const char *proname);
 extern Oid	get_fn_expr_rettype(FmgrInfo *flinfo);
 extern Oid	get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);
 extern Oid	get_call_expr_argtype(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 bc80a51..084a38a 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1032,6 +1032,9 @@ plperl_validator(PG_FUNCTION_ARGS)
 
 	plperl_init_all();
 
+	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+		PG_RETURN_VOID();
+
 	/* Get the new function's pg_proc entry */
 	tuple = SearchSysCache(PROCOID,
 						   ObjectIdGetDatum(funcoid),
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 43446f0..04ebcaf 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -170,6 +170,9 @@ plpgsql_validator(PG_FUNCTION_ARGS)
 	/* perform initialization */
 	plpgsql_init_all();
 
+	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+		PG_RETURN_VOID();
+
 	/* Get the new function's pg_proc entry */
 	tuple = SearchSysCache(PROCOID,
 						   ObjectIdGetDatum(funcoid),