aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/extension.c
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2023-08-07 06:05:56 -0700
committerNoah Misch <noah@leadboat.com>2023-08-07 06:05:59 -0700
commitf53511010b72d7d314e22be7c63ef94792fee345 (patch)
tree54d4406af5746be10436d1afdfe269b47f899226 /src/backend/commands/extension.c
parente8386b2cef7741aa4e49870691d67f792d5f6789 (diff)
downloadpostgresql-f53511010b72d7d314e22be7c63ef94792fee345.tar.gz
postgresql-f53511010b72d7d314e22be7c63ef94792fee345.zip
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
Diffstat (limited to 'src/backend/commands/extension.c')
-rw-r--r--src/backend/commands/extension.c27
1 files changed, 27 insertions, 0 deletions
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 39db7584f33..2ff0d691d86 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1001,6 +1001,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);
@@ -1030,6 +1040,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)));
}
/*
@@ -1041,6 +1056,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,
@@ -1048,6 +1064,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)));
}
/*
@@ -1057,6 +1078,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);
@@ -1069,6 +1091,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)));
}
/*