mirror of
https://github.com/postgres/postgres.git
synced 2025-07-08 11:42:09 +03:00
Reject substituting extension schemas or owners matching ["$'\].
Substituting such values in extension scripts facilitated SQL injection when @extowner@, @extschema@, or @extschema:...@ appeared inside a quoting construct (dollar quoting, '', or ""). No bundled extension was vulnerable. Vulnerable uses do appear in a documentation example and in non-bundled extensions. Hence, the attack prerequisite was an administrator having installed files of a vulnerable, trusted, non-bundled extension. Subject to that prerequisite, this enabled an attacker having database-level CREATE privilege to execute arbitrary code as the bootstrap superuser. By blocking this attack in the core server, there's no need to modify individual extensions. Back-patch to v11 (all supported versions). Reported by Micah Gate, Valerie Woolard, Tim Carey-Smith, and Christoph Berg. Security: CVE-2023-39417
This commit is contained in:
@ -996,6 +996,16 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
||||
char *c_sql = read_extension_script_file(control, filename);
|
||||
Datum t_sql;
|
||||
|
||||
/*
|
||||
* We filter each substitution through quote_identifier(). When the
|
||||
* arg contains one of the following characters, no one collection of
|
||||
* quoting can work inside $$dollar-quoted string literals$$,
|
||||
* 'single-quoted string literals', and outside of any literal. To
|
||||
* avoid a security snare for extension authors, error on substitution
|
||||
* for arguments containing these.
|
||||
*/
|
||||
const char *quoting_relevant_chars = "\"$'\\";
|
||||
|
||||
/* We use various functions that want to operate on text datums */
|
||||
t_sql = CStringGetTextDatum(c_sql);
|
||||
|
||||
@ -1025,6 +1035,11 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
||||
t_sql,
|
||||
CStringGetTextDatum("@extowner@"),
|
||||
CStringGetTextDatum(qUserName));
|
||||
if (strpbrk(userName, quoting_relevant_chars))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid character in extension owner: must not contain any of \"%s\"",
|
||||
quoting_relevant_chars)));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1036,6 +1051,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
||||
*/
|
||||
if (!control->relocatable)
|
||||
{
|
||||
Datum old = t_sql;
|
||||
const char *qSchemaName = quote_identifier(schemaName);
|
||||
|
||||
t_sql = DirectFunctionCall3Coll(replace_text,
|
||||
@ -1043,6 +1059,11 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
||||
t_sql,
|
||||
CStringGetTextDatum("@extschema@"),
|
||||
CStringGetTextDatum(qSchemaName));
|
||||
if (t_sql != old && strpbrk(schemaName, quoting_relevant_chars))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
|
||||
control->name, quoting_relevant_chars)));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1052,6 +1073,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
||||
Assert(list_length(control->requires) == list_length(requiredSchemas));
|
||||
forboth(lc, control->requires, lc2, requiredSchemas)
|
||||
{
|
||||
Datum old = t_sql;
|
||||
char *reqextname = (char *) lfirst(lc);
|
||||
Oid reqschema = lfirst_oid(lc2);
|
||||
char *schemaName = get_namespace_name(reqschema);
|
||||
@ -1064,6 +1086,11 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
||||
t_sql,
|
||||
CStringGetTextDatum(repltoken),
|
||||
CStringGetTextDatum(qSchemaName));
|
||||
if (t_sql != old && strpbrk(schemaName, quoting_relevant_chars))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
|
||||
reqextname, quoting_relevant_chars)));
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user