diff options
author | Noah Misch <noah@leadboat.com> | 2023-08-07 06:05:56 -0700 |
---|---|---|
committer | Noah Misch <noah@leadboat.com> | 2023-08-07 06:05:59 -0700 |
commit | f53511010b72d7d314e22be7c63ef94792fee345 (patch) | |
tree | 54d4406af5746be10436d1afdfe269b47f899226 /src/backend/commands/extension.c | |
parent | e8386b2cef7741aa4e49870691d67f792d5f6789 (diff) | |
download | postgresql-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.c | 27 |
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))); } /* |