Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/tleextension.c
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,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);

Expand Down Expand Up @@ -1421,6 +1431,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)));
}

/*
Expand All @@ -1432,13 +1447,19 @@ 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,
C_COLLATION_OID,
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)));
}

/*
Expand Down
75 changes: 75 additions & 0 deletions test/expected/pg_tle_injection.out
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,81 @@ $_pg_tle_$
);
ERROR: invalid extension name: "test9.control"(),pg_sleep(10),pgtle."test9"
DETAIL: Extension names must only contain alphanumeric characters or valid separators.
-- @extschema@ and @extowner@ substitutions are filtered through
-- quote_identifier(). A schema or owner name containing a character that
-- cannot be consistently quoted inside and outside of string literals (any of
-- " $ ' \) must be rejected rather than substituted into the script.
-- An extension whose script references @extschema@ cannot be created into a
-- schema whose name contains a quoting-relevant character.
SELECT pgtle.install_extension
(
'ext_schema_subst',
'1.0',
'references @extschema@',
$_pgtle_$
CREATE FUNCTION whereami() RETURNS text AS $$ SELECT '@extschema@' $$ LANGUAGE SQL;
$_pgtle_$
);
install_extension
-------------------
t
(1 row)

CREATE SCHEMA "bad""schema";
-- this should fail
CREATE EXTENSION ext_schema_subst SCHEMA "bad""schema";
ERROR: invalid character in extension "ext_schema_subst" schema: must not contain any of ""$'\"
-- a schema with an ordinary name still works
CREATE SCHEMA good_schema;
CREATE EXTENSION ext_schema_subst SCHEMA good_schema;
SELECT good_schema.whereami();
whereami
-------------
good_schema
(1 row)

DROP EXTENSION ext_schema_subst;
SELECT pgtle.uninstall_extension('ext_schema_subst');
uninstall_extension
---------------------
t
(1 row)

DROP SCHEMA "bad""schema";
DROP SCHEMA good_schema;
-- An extension whose script references @extowner@ cannot be created by a role
-- whose name contains a quoting-relevant character. (The role is created as a
-- superuser only to avoid unrelated privilege setup; the substitution and its
-- validation run regardless of the caller's privileges.)
CREATE ROLE " owner'" SUPERUSER LOGIN;
SELECT pgtle.install_extension
(
'ext_owner_subst',
'1.0',
'references @extowner@',
$_pgtle_$
CREATE FUNCTION owned_by() RETURNS text AS $$ SELECT '@extowner@' $$ LANGUAGE SQL;
$_pgtle_$
);
install_extension
-------------------
t
(1 row)

SET SESSION AUTHORIZATION " owner'";
CREATE SCHEMA owner_schema;
-- this should fail
CREATE EXTENSION ext_owner_subst SCHEMA owner_schema;
ERROR: invalid character in extension owner: must not contain any of ""$'\"
RESET SESSION AUTHORIZATION;
SELECT pgtle.uninstall_extension('ext_owner_subst');
uninstall_extension
---------------------
t
(1 row)

DROP SCHEMA owner_schema;
DROP ROLE " owner'";
-- cleanup
DROP EXTENSION pg_tle;
DROP SCHEMA pgtle;
Expand Down
51 changes: 51 additions & 0 deletions test/sql/pg_tle_injection.sql
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,57 @@ $_pg_tle_$
$_pg_tle_$
);

-- @extschema@ and @extowner@ substitutions are filtered through
-- quote_identifier(). A schema or owner name containing a character that
-- cannot be consistently quoted inside and outside of string literals (any of
-- " $ ' \) must be rejected rather than substituted into the script.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: guess it is still beneficial to test $ and \ ?


-- An extension whose script references @extschema@ cannot be created into a
-- schema whose name contains a quoting-relevant character.
SELECT pgtle.install_extension
(
'ext_schema_subst',
'1.0',
'references @extschema@',
$_pgtle_$
CREATE FUNCTION whereami() RETURNS text AS $$ SELECT '@extschema@' $$ LANGUAGE SQL;
$_pgtle_$
);
CREATE SCHEMA "bad""schema";
-- this should fail
CREATE EXTENSION ext_schema_subst SCHEMA "bad""schema";
-- a schema with an ordinary name still works
CREATE SCHEMA good_schema;
CREATE EXTENSION ext_schema_subst SCHEMA good_schema;
SELECT good_schema.whereami();
DROP EXTENSION ext_schema_subst;
SELECT pgtle.uninstall_extension('ext_schema_subst');
DROP SCHEMA "bad""schema";
DROP SCHEMA good_schema;

-- An extension whose script references @extowner@ cannot be created by a role
-- whose name contains a quoting-relevant character. (The role is created as a
-- superuser only to avoid unrelated privilege setup; the substitution and its
-- validation run regardless of the caller's privileges.)
CREATE ROLE " owner'" SUPERUSER LOGIN;
SELECT pgtle.install_extension
(
'ext_owner_subst',
'1.0',
'references @extowner@',
$_pgtle_$
CREATE FUNCTION owned_by() RETURNS text AS $$ SELECT '@extowner@' $$ LANGUAGE SQL;
$_pgtle_$
);
SET SESSION AUTHORIZATION " owner'";
CREATE SCHEMA owner_schema;
-- this should fail
CREATE EXTENSION ext_owner_subst SCHEMA owner_schema;
RESET SESSION AUTHORIZATION;
SELECT pgtle.uninstall_extension('ext_owner_subst');
DROP SCHEMA owner_schema;
DROP ROLE " owner'";

-- cleanup
DROP EXTENSION pg_tle;
DROP SCHEMA pgtle;
Expand Down
Loading