On Wed, May 30, 2012 at 07:34:16PM -0400, Noah Misch wrote:
> ALTER FUNCTION OWNER TO on a C-language function conveys more trust than
> meets the eye:
>
> BEGIN;
> CREATE ROLE alice;
> CREATE FUNCTION mylen(text) RETURNS integer LANGUAGE internal IMMUTABLE
> STRICT AS 'textlen';
> ALTER FUNCTION mylen(text) OWNER TO alice;
> COMMIT;
>
> SET SESSION AUTHORIZATION alice;
> ALTER FUNCTION mylen(text) CALLED ON NULL INPUT;
> SELECT mylen(NULL); -- SIGSEGV
>
> CREATE FUNCTION + ALTER FUNCTION OWNER TO is useful for creating another
> user's untrusted-language SECURITY DEFINER function. ALTER FUNCTION CALLED ON
> NULL INPUT ought to require that the user be eligible to redefine the function
> completely.
Here's a patch implementing that restriction. To clarify, I see no need to
repeat *all* the CREATE-time checks; for example, there's no need to recheck
permission to use the return type. The language usage check is enough.
I didn't feel the need to memorialize a test like the above in an actual
regression test, but that's the one I used to verify the change.
Considering the crash potential, I'd recommend backpatching this.
Thanks,
nm
diff --git a/doc/src/sgml/ref/alter_function.sgml
b/doc/src/sgml/ref/alter_function.sgml
index 013b6f8..cfc2a69 100644
*** a/doc/src/sgml/ref/alter_function.sgml
--- b/doc/src/sgml/ref/alter_function.sgml
***************
*** 54,59 **** ALTER FUNCTION <replaceable>name</replaceable> ( [ [
<replaceable class="paramet
--- 54,61 ----
<para>
You must own the function to use <command>ALTER FUNCTION</>.
+ Marking a function <literal>CALLED ON NULL INPUT</> also requires
+ permission to use the function's language when creating new functions.
To change a function's schema, you must also have <literal>CREATE</>
privilege on the new schema.
To alter the owner, you must also be a direct or indirect member of the new
diff --git a/src/backend/commands/functioncindex 13e30f4..b40bcdf 100644
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 70,75 **** static void AlterFunctionOwner_internal(Relation rel, HeapTuple
tup,
--- 70,107 ----
/*
+ * May the current user create functions of a given language?
+ */
+ static void
+ check_language_usage(HeapTuple languageTuple)
+ {
+ Oid languageOid;
+ Form_pg_language languageStruct;
+
+ languageOid = HeapTupleGetOid(languageTuple);
+ languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
+
+ if (languageStruct->lanpltrusted)
+ {
+ /* if trusted language, need USAGE privilege */
+ AclResult aclresult;
+
+ aclresult = pg_language_aclcheck(languageOid, GetUserId(),
ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_LANGUAGE,
+
NameStr(languageStruct->lanname));
+ }
+ else
+ {
+ /* if untrusted language, must be superuser */
+ if (!superuser())
+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_LANGUAGE,
+
NameStr(languageStruct->lanname));
+ }
+ }
+
+
+ /*
* Examine the RETURNS clause of the CREATE FUNCTION statement
* and return information about it as *prorettype_p and *returnsSet.
*
***************
*** 864,890 **** CreateFunction(CreateFunctionStmt *stmt, const char
*queryString)
(PLTemplateExists(language) ?
errhint("Use CREATE LANGUAGE to load the
language into the database.") : 0)));
languageOid = HeapTupleGetOid(languageTuple);
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
-
- if (languageStruct->lanpltrusted)
- {
- /* if trusted language, need USAGE privilege */
- AclResult aclresult;
-
- aclresult = pg_language_aclcheck(languageOid, GetUserId(),
ACL_USAGE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_LANGUAGE,
-
NameStr(languageStruct->lanname));
- }
- else
- {
- /* if untrusted language, must be superuser */
- if (!superuser())
- aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_LANGUAGE,
-
NameStr(languageStruct->lanname));
- }
-
languageValidator = languageStruct->lanvalidator;
ReleaseSysCache(languageTuple);
--- 896,905 ----
(PLTemplateExists(language) ?
errhint("Use CREATE LANGUAGE to load the
language into the database.") : 0)));
+ check_language_usage(languageTuple);
+
languageOid = HeapTupleGetOid(languageTuple);
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
languageValidator = languageStruct->lanvalidator;
ReleaseSysCache(languageTuple);
***************
*** 1312,1318 **** AlterFunction(AlterFunctionStmt *stmt)
--- 1327,1355 ----
if (volatility_item)
procForm->provolatile =
interpret_func_volatility(volatility_item);
if (strict_item)
+ {
+ /*
+ * C-language functions that expect to be declared STRICT often
omit
+ * NULL argument checks, crashing if they do receive a NULL. To
+ * protect such functions against less-privileged owners,
clearing
+ * proisstrict requires the authority to define a new function
of the
+ * same language.
+ */
+ if (!intVal(strict_item->arg))
+ {
+ HeapTuple langTuple;
+
+ langTuple = SearchSysCache1(LANGOID,
+
PointerGetDatum(procForm->prolang));
+ if (!HeapTupleIsValid(langTuple))
+ elog(ERROR, "cache lookup failed for language
%u",
+ procForm->prolang);
+ check_language_usage(langTuple);
+ ReleaseSysCache(langTuple);
+ }
+
procForm->proisstrict = intVal(strict_item->arg);
+ }
if (security_def_item)
procForm->prosecdef = intVal(security_def_item->arg);
if (leakproof_item)
***************
*** 1974,2002 **** ExecuteDoStmt(DoStmt *stmt)
(PLTemplateExists(language) ?
errhint("Use CREATE LANGUAGE to load the
language into the database.") : 0)));
codeblock->langOid = HeapTupleGetOid(languageTuple);
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
codeblock->langIsTrusted = languageStruct->lanpltrusted;
- if (languageStruct->lanpltrusted)
- {
- /* if trusted language, need USAGE privilege */
- AclResult aclresult;
-
- aclresult = pg_language_aclcheck(codeblock->langOid,
GetUserId(),
-
ACL_USAGE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_LANGUAGE,
-
NameStr(languageStruct->lanname));
- }
- else
- {
- /* if untrusted language, must be superuser */
- if (!superuser())
- aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_LANGUAGE,
-
NameStr(languageStruct->lanname));
- }
-
/* get the handler function's OID */
laninline = languageStruct->laninline;
if (!OidIsValid(laninline))
--- 2011,2022 ----
(PLTemplateExists(language) ?
errhint("Use CREATE LANGUAGE to load the
language into the database.") : 0)));
+ check_language_usage(languageTuple);
+
codeblock->langOid = HeapTupleGetOid(languageTuple);
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
codeblock->langIsTrusted = languageStruct->lanpltrusted;
/* get the handler function's OID */
laninline = languageStruct->laninline;
if (!OidIsValid(laninline))
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers