diff --git a/contrib/citext/citext--unpackaged--1.0.sql b/contrib/citext/citext--unpackaged--1.0.sql
index ef6d6b0639..4061a0aeff 100644
--- a/contrib/citext/citext--unpackaged--1.0.sql
+++ b/contrib/citext/citext--unpackaged--1.0.sql
@@ -89,8 +89,17 @@ ALTER EXTENSION citext ADD function translate(citext,citext,text);
-- default collation is pinned.
--
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
+
WITH RECURSIVE typeoids(typoid) AS
- ( SELECT 'citext'::pg_catalog.regtype UNION
+ ( SELECT (my_schema || '.citext')::pg_catalog.regtype UNION
SELECT oid FROM pg_catalog.pg_type, typeoids
WHERE typelem = typoid OR typbasetype = typoid )
UPDATE pg_catalog.pg_type SET typcollation = 100
@@ -98,7 +107,7 @@ FROM typeoids
WHERE oid = typeoids.typoid;
WITH RECURSIVE typeoids(typoid) AS
- ( SELECT 'citext'::pg_catalog.regtype UNION
+ ( SELECT (my_schema || '.citext')::pg_catalog.regtype UNION
SELECT oid FROM pg_catalog.pg_type, typeoids
WHERE typelem = typoid OR typbasetype = typoid )
UPDATE pg_catalog.pg_attribute SET attcollation = 100
@@ -113,7 +122,7 @@ UPDATE pg_catalog.pg_index SET indcollation =
pg_catalog.regexp_replace(indcollation::pg_catalog.text, '^0', '100')::pg_catalog.oidvector
WHERE indclass[0] IN (
WITH RECURSIVE typeoids(typoid) AS
- ( SELECT 'citext'::pg_catalog.regtype UNION
+ ( SELECT (my_schema || '.citext')::pg_catalog.regtype UNION
SELECT oid FROM pg_catalog.pg_type, typeoids
WHERE typelem = typoid OR typbasetype = typoid )
SELECT oid FROM pg_catalog.pg_opclass, typeoids
@@ -124,7 +133,7 @@ UPDATE pg_catalog.pg_index SET indcollation =
pg_catalog.regexp_replace(indcollation::pg_catalog.text, E'^(\\d+) 0', E'\\1 100')::pg_catalog.oidvector
WHERE indclass[1] IN (
WITH RECURSIVE typeoids(typoid) AS
- ( SELECT 'citext'::pg_catalog.regtype UNION
+ ( SELECT (my_schema || '.citext')::pg_catalog.regtype UNION
SELECT oid FROM pg_catalog.pg_type, typeoids
WHERE typelem = typoid OR typbasetype = typoid )
SELECT oid FROM pg_catalog.pg_opclass, typeoids
@@ -135,7 +144,7 @@ UPDATE pg_catalog.pg_index SET indcollation =
pg_catalog.regexp_replace(indcollation::pg_catalog.text, E'^(\\d+ \\d+) 0', E'\\1 100')::pg_catalog.oidvector
WHERE indclass[2] IN (
WITH RECURSIVE typeoids(typoid) AS
- ( SELECT 'citext'::pg_catalog.regtype UNION
+ ( SELECT (my_schema || '.citext')::pg_catalog.regtype UNION
SELECT oid FROM pg_catalog.pg_type, typeoids
WHERE typelem = typoid OR typbasetype = typoid )
SELECT oid FROM pg_catalog.pg_opclass, typeoids
@@ -146,7 +155,7 @@ UPDATE pg_catalog.pg_index SET indcollation =
pg_catalog.regexp_replace(indcollation::pg_catalog.text, E'^(\\d+ \\d+ \\d+) 0', E'\\1 100')::pg_catalog.oidvector
WHERE indclass[3] IN (
WITH RECURSIVE typeoids(typoid) AS
- ( SELECT 'citext'::pg_catalog.regtype UNION
+ ( SELECT (my_schema || '.citext')::pg_catalog.regtype UNION
SELECT oid FROM pg_catalog.pg_type, typeoids
WHERE typelem = typoid OR typbasetype = typoid )
SELECT oid FROM pg_catalog.pg_opclass, typeoids
@@ -157,7 +166,7 @@ UPDATE pg_catalog.pg_index SET indcollation =
pg_catalog.regexp_replace(indcollation::pg_catalog.text, E'^(\\d+ \\d+ \\d+ \\d+) 0', E'\\1 100')::pg_catalog.oidvector
WHERE indclass[4] IN (
WITH RECURSIVE typeoids(typoid) AS
- ( SELECT 'citext'::pg_catalog.regtype UNION
+ ( SELECT (my_schema || '.citext')::pg_catalog.regtype UNION
SELECT oid FROM pg_catalog.pg_type, typeoids
WHERE typelem = typoid OR typbasetype = typoid )
SELECT oid FROM pg_catalog.pg_opclass, typeoids
@@ -168,7 +177,7 @@ UPDATE pg_catalog.pg_index SET indcollation =
pg_catalog.regexp_replace(indcollation::pg_catalog.text, E'^(\\d+ \\d+ \\d+ \\d+ \\d+) 0', E'\\1 100')::pg_catalog.oidvector
WHERE indclass[5] IN (
WITH RECURSIVE typeoids(typoid) AS
- ( SELECT 'citext'::pg_catalog.regtype UNION
+ ( SELECT (my_schema || '.citext')::pg_catalog.regtype UNION
SELECT oid FROM pg_catalog.pg_type, typeoids
WHERE typelem = typoid OR typbasetype = typoid )
SELECT oid FROM pg_catalog.pg_opclass, typeoids
@@ -179,7 +188,7 @@ UPDATE pg_catalog.pg_index SET indcollation =
pg_catalog.regexp_replace(indcollation::pg_catalog.text, E'^(\\d+ \\d+ \\d+ \\d+ \\d+ \\d+) 0', E'\\1 100')::pg_catalog.oidvector
WHERE indclass[6] IN (
WITH RECURSIVE typeoids(typoid) AS
- ( SELECT 'citext'::pg_catalog.regtype UNION
+ ( SELECT (my_schema || '.citext')::pg_catalog.regtype UNION
SELECT oid FROM pg_catalog.pg_type, typeoids
WHERE typelem = typoid OR typbasetype = typoid )
SELECT oid FROM pg_catalog.pg_opclass, typeoids
@@ -190,7 +199,7 @@ UPDATE pg_catalog.pg_index SET indcollation =
pg_catalog.regexp_replace(indcollation::pg_catalog.text, E'^(\\d+ \\d+ \\d+ \\d+ \\d+ \\d+ \\d+) 0', E'\\1 100')::pg_catalog.oidvector
WHERE indclass[7] IN (
WITH RECURSIVE typeoids(typoid) AS
- ( SELECT 'citext'::pg_catalog.regtype UNION
+ ( SELECT (my_schema || '.citext')::pg_catalog.regtype UNION
SELECT oid FROM pg_catalog.pg_type, typeoids
WHERE typelem = typoid OR typbasetype = typoid )
SELECT oid FROM pg_catalog.pg_opclass, typeoids
@@ -198,3 +207,7 @@ WHERE indclass[7] IN (
);
-- somewhat arbitrarily, we assume no citext indexes have more than 8 columns
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
diff --git a/contrib/earthdistance/earthdistance--1.0.sql b/contrib/earthdistance/earthdistance--1.0.sql
index c7e9429b61..901b2b0610 100644
--- a/contrib/earthdistance/earthdistance--1.0.sql
+++ b/contrib/earthdistance/earthdistance--1.0.sql
@@ -31,7 +31,7 @@ CREATE DOMAIN earth AS cube
CONSTRAINT not_point check(cube_is_point(value))
CONSTRAINT not_3d check(cube_dim(value) <= 3)
CONSTRAINT on_surface check(abs(cube_distance(value, '(0)'::cube) /
- earth() - 1) < '10e-7'::float8);
+ earth() - '1'::float8) < '10e-7'::float8);
CREATE FUNCTION sec_to_gc(float8)
RETURNS float8
diff --git a/contrib/hstore/hstore--1.1--1.2.sql b/contrib/hstore/hstore--1.1--1.2.sql
index a868ffe48e..cc69fc7f80 100644
--- a/contrib/hstore/hstore--1.1--1.2.sql
+++ b/contrib/hstore/hstore--1.1--1.2.sql
@@ -9,10 +9,13 @@
-- dependent on the extension.
DO LANGUAGE plpgsql
-
$$
-
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
PERFORM 1
FROM pg_proc p
@@ -27,6 +30,7 @@ BEGIN
IF NOT FOUND
THEN
+ PERFORM pg_catalog.set_config('search_path', old_path, true);
CREATE FUNCTION hstore_to_json(hstore)
RETURNS json
@@ -43,6 +47,7 @@ BEGIN
END IF;
+PERFORM pg_catalog.set_config('search_path', old_path, true);
END;
$$;
diff --git a/contrib/intarray/intarray--unpackaged--1.0.sql b/contrib/intarray/intarray--unpackaged--1.0.sql
index 63814cef98..7f1bef60b1 100644
--- a/contrib/intarray/intarray--unpackaged--1.0.sql
+++ b/contrib/intarray/intarray--unpackaged--1.0.sql
@@ -84,13 +84,23 @@ ALTER EXTENSION intarray ADD function ginint4_consistent(internal,smallint,inter
-- entries. This is ugly as can be, but there's no other way to do it
-- while preserving the identities (OIDs) of the functions.
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema_unquoted pg_catalog.text := pg_catalog.current_schema();
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
+
UPDATE pg_catalog.pg_proc
SET pronargs = 7, proargtypes = '2281 2281 21 2281 2281 2281 2281'
-WHERE oid = 'ginint4_queryextract(internal,internal,smallint,internal,internal)'::pg_catalog.regprocedure;
+WHERE oid = (my_schema || '.ginint4_queryextract(internal,internal,smallint,internal,internal)')::pg_catalog.regprocedure;
UPDATE pg_catalog.pg_proc
SET pronargs = 8, proargtypes = '2281 21 2281 23 2281 2281 2281 2281'
-WHERE oid = 'ginint4_consistent(internal,smallint,internal,integer,internal,internal)'::pg_catalog.regprocedure;
+WHERE oid = (my_schema || '.ginint4_consistent(internal,smallint,internal,integer,internal,internal)')::pg_catalog.regprocedure;
-- intarray also relies on the core function ginarrayextract, which changed
-- signature in 9.1. To support upgrading, pg_catalog contains entries
@@ -104,8 +114,12 @@ SET amproc = 'pg_catalog.ginarrayextract(anyarray,internal,internal)'::pg_catalo
WHERE amprocfamily =
(SELECT oid FROM pg_catalog.pg_opfamily WHERE opfname = 'gin__int_ops' AND
opfnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname = pg_catalog.current_schema()))
+ WHERE nspname = my_schema_unquoted))
AND amproclefttype = 'integer[]'::pg_catalog.regtype
AND amprocrighttype = 'integer[]'::pg_catalog.regtype
AND amprocnum = 2
AND amproc = 'pg_catalog.ginarrayextract(anyarray,internal)'::pg_catalog.regprocedure;
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
diff --git a/contrib/pg_trgm/pg_trgm--unpackaged--1.0.sql b/contrib/pg_trgm/pg_trgm--unpackaged--1.0.sql
index d3eab97d41..99444d37c3 100644
--- a/contrib/pg_trgm/pg_trgm--unpackaged--1.0.sql
+++ b/contrib/pg_trgm/pg_trgm--unpackaged--1.0.sql
@@ -57,13 +57,26 @@ LANGUAGE C IMMUTABLE STRICT;
-- entries. This is ugly as can be, but there's no other way to do it
-- while preserving the identities (OIDs) of the functions.
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
+
UPDATE pg_catalog.pg_proc
SET pronargs = 7, proargtypes = '25 2281 21 2281 2281 2281 2281'
-WHERE oid = 'gin_extract_query_trgm(text,internal,int2,internal,internal)'::pg_catalog.regprocedure;
+WHERE oid = (my_schema || '.gin_extract_query_trgm(text,internal,int2,internal,internal)')::pg_catalog.regprocedure;
UPDATE pg_catalog.pg_proc
SET pronargs = 8, proargtypes = '2281 21 25 23 2281 2281 2281 2281'
-WHERE oid = 'gin_trgm_consistent(internal,smallint,text,integer,internal,internal)'::pg_catalog.regprocedure;
+WHERE oid = (my_schema || '.gin_trgm_consistent(internal,smallint,text,integer,internal,internal)')::pg_catalog.regprocedure;
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
-- These were not in 9.0:
diff --git a/contrib/tsearch2/tsearch2--unpackaged--1.0.sql b/contrib/tsearch2/tsearch2--unpackaged--1.0.sql
index e123297132..7a66d5982f 100644
--- a/contrib/tsearch2/tsearch2--unpackaged--1.0.sql
+++ b/contrib/tsearch2/tsearch2--unpackaged--1.0.sql
@@ -110,12 +110,22 @@ ALTER EXTENSION tsearch2 ADD operator class @extschema@.tsquery_ops using btree;
-- Avert your eyes while we hack the pg_amproc entries to make them link to
-- the new forms ...
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema_unquoted pg_catalog.text := pg_catalog.current_schema();
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
+
UPDATE pg_catalog.pg_amproc
SET amproc = 'pg_catalog.gin_extract_tsvector(pg_catalog.tsvector,internal,internal)'::pg_catalog.regprocedure
WHERE amprocfamily =
(SELECT oid FROM pg_catalog.pg_opfamily WHERE opfname = 'gin_tsvector_ops' AND
opfnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname = '@extschema@'))
+ WHERE nspname = my_schema_unquoted))
AND amproclefttype = 'pg_catalog.tsvector'::pg_catalog.regtype
AND amprocrighttype = 'pg_catalog.tsvector'::pg_catalog.regtype
AND amprocnum = 2
@@ -126,7 +136,7 @@ SET amproc = 'pg_catalog.gin_extract_tsquery(pg_catalog.tsquery,internal,smallin
WHERE amprocfamily =
(SELECT oid FROM pg_catalog.pg_opfamily WHERE opfname = 'gin_tsvector_ops' AND
opfnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname = '@extschema@'))
+ WHERE nspname = my_schema_unquoted))
AND amproclefttype = 'pg_catalog.tsvector'::pg_catalog.regtype
AND amprocrighttype = 'pg_catalog.tsvector'::pg_catalog.regtype
AND amprocnum = 3
@@ -137,8 +147,12 @@ SET amproc = 'pg_catalog.gin_tsquery_consistent(internal,smallint,pg_catalog.tsq
WHERE amprocfamily =
(SELECT oid FROM pg_catalog.pg_opfamily WHERE opfname = 'gin_tsvector_ops' AND
opfnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname = '@extschema@'))
+ WHERE nspname = my_schema_unquoted))
AND amproclefttype = 'pg_catalog.tsvector'::pg_catalog.regtype
AND amprocrighttype = 'pg_catalog.tsvector'::pg_catalog.regtype
AND amprocnum = 4
AND amproc = 'pg_catalog.gin_tsquery_consistent(internal,smallint,pg_catalog.tsquery,integer,internal,internal)'::pg_catalog.regprocedure;
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
diff --git a/doc/src/sgml/earthdistance.sgml b/doc/src/sgml/earthdistance.sgml
index 2b6df5fee5..d01f8ffa7d 100644
--- a/doc/src/sgml/earthdistance.sgml
+++ b/doc/src/sgml/earthdistance.sgml
@@ -10,9 +10,8 @@
The earthdistance> module provides two different approaches to
calculating great circle distances on the surface of the Earth. The one
- described first depends on the cube> module (which
- must> be installed before earthdistance> can be
- installed). The second one is based on the built-in point> data type,
+ described first depends on the cube module.
+ The second one is based on the built-in point data type,
using longitude and latitude for the coordinates.
@@ -23,6 +22,26 @@
project.)
+
+ The cube module must be installed
+ before earthdistance can be installed.
+
+
+
+
+ It is strongly recommended that earthdistance
+ and cube be installed in the same schema, and that
+ that schema be one for which CREATE privilege has not been and will not
+ be granted to any untrusted users.
+ Otherwise there are installation-time security hazards
+ if earthdistance's schema contains objects defined
+ by a hostile user.
+ Furthermore, when using earthdistance's functions
+ after installation, the entire search path should contain only trusted
+ schemas.
+
+
+
Cube-based Earth Distances
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index afd2b8f9fc..eb0d55f3e5 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -380,32 +380,6 @@
schema(s) its member objects are within.
-
- Defining Extension Objects
-
-
-
- Widely-distributed extensions should assume little about the database
- they occupy. In particular, unless you issued SET search_path =
- pg_temp, assume each unqualified name could resolve to an
- object that a malicious user has defined. Beware of constructs that
- depend on search_path implicitly: IN
- and CASE expression WHEN
- always select an operator using the search path. In their place, use
- OPERATOR(schema.=) ANY
- and CASE WHEN expression.
-
-
-
-
Extension Files
@@ -661,7 +635,7 @@
schema; that is, CREATE EXTENSION> does the equivalent of
this:
-SET LOCAL search_path TO @extschema@;
+SET LOCAL search_path TO @extschema@, pg_temp;
This allows the objects created by the script file to go into the target
schema. The script file can change search_path> if it wishes,
@@ -681,9 +655,15 @@ SET LOCAL search_path TO @extschema@;
If any prerequisite extensions are listed in requires
- in the control file, their target schemas are appended to the initial
- setting of search_path>. This allows their objects to be
- visible to the new extension's script file.
+ in the control file, their target schemas are added to the initial
+ setting of search_path, following the new
+ extension's target schema. This allows their objects to be visible to
+ the new extension's script file.
+
+
+
+ For security, pg_temp is automatically appended to
+ the end of search_path in all cases.
@@ -880,7 +860,144 @@ SELECT * FROM pg_extension_update_paths('extension_name>');
-
+
+ Security Considerations for Extensions
+
+
+ Widely-distributed extensions should assume little about the database
+ they occupy. Therefore, it's appropriate to write functions provided
+ by an extension in a secure style that cannot be compromised by
+ search-path-based attacks.
+
+
+
+ An extension that has the superuser property set to
+ true must also consider security hazards for the actions taken within
+ its installation and update scripts. It is not terribly difficult for
+ a malicious user to create trojan-horse objects that will compromise
+ later execution of a carelessly-written extension script, allowing that
+ user to acquire superuser privileges.
+
+
+
+ Advice about writing functions securely is provided in
+ below, and advice
+ about writing installation scripts securely is provided in
+ .
+
+
+
+ Security Considerations for Extension Functions
+
+
+ SQL-language and PL-language functions provided by extensions are at
+ risk of search-path-based attacks when they are executed, since
+ parsing of these functions occurs at execution time not creation time.
+
+
+
+ The CREATE
+ FUNCTION reference page contains advice about
+ writing SECURITY DEFINER functions safely. It's
+ good practice to apply those techniques for any function provided by
+ an extension, since the function might be called by a high-privilege
+ user.
+
+
+
+
+ If you cannot set the search_path to contain only
+ secure schemas, assume that each unqualified name could resolve to an
+ object that a malicious user has defined. Beware of constructs that
+ depend on search_path implicitly; for
+ example, IN
+ and CASE expression WHEN
+ always select an operator using the search path. In their place, use
+ OPERATOR(schema.=) ANY
+ and CASE WHEN expression.
+
+
+
+ A general-purpose extension usually should not assume that it's been
+ installed into a secure schema, which means that even schema-qualified
+ references to its own objects are not entirely risk-free. For
+ example, if the extension has defined a
+ function myschema.myfunc(bigint) then a call such
+ as myschema.myfunc(42) could be captured by a
+ hostile function myschema.myfunc(integer). Be
+ careful that the data types of function and operator parameters exactly
+ match the declared argument types, using explicit casts where necessary.
+
+
+
+
+ Security Considerations for Extension Scripts
+
+
+ An extension installation or update script should be written to guard
+ against search-path-based attacks occurring when the script executes.
+ If an object reference in the script can be made to resolve to some
+ other object than the script author intended, then a compromise might
+ occur immediately, or later when the mis-defined extension object is
+ used.
+
+
+
+ DDL commands such as CREATE FUNCTION
+ and CREATE OPERATOR CLASS are generally secure,
+ but beware of any command having a general-purpose expression as a
+ component. For example, CREATE VIEW needs to be
+ vetted, as does a DEFAULT expression
+ in CREATE FUNCTION.
+
+
+
+ Sometimes an extension script might need to execute general-purpose
+ SQL, for example to make catalog adjustments that aren't possible via
+ DDL. Be careful to execute such commands with a
+ secure search_path; do not
+ trust the path provided by CREATE/ALTER EXTENSION
+ to be secure. Best practice is to temporarily
+ set search_path to 'pg_catalog,
+ pg_temp' and insert references to the extension's
+ installation schema explicitly where needed. (This practice might
+ also be helpful for creating views.) Examples can be found in
+ the contrib modules in
+ the PostgreSQL source code distribution.
+
+
+
+ Cross-extension references are extremely difficult to make fully
+ secure, partially because of uncertainty about which schema the other
+ extension is in. The hazards are reduced if both extensions are
+ installed in the same schema, because then a hostile object cannot be
+ placed ahead of the referenced extension in the installation-time
+ search_path. However, no mechanism currently exists
+ to require that.
+
+
+
+ Do not use CREATE OR REPLACE
+ FUNCTION, except in an update script that must change the
+ definition of a function that is known to be an extension member
+ already. (Likewise for other OR REPLACE options.)
+ Using OR REPLACE unnecessarily not only has a risk
+ of accidentally overwriting someone else's function, but it creates a
+ security hazard since the overwritten function would still be owned by
+ its original owner, who could modify it.
+
+
+
+
+ Extension Example
@@ -899,18 +1016,18 @@ SELECT * FROM pg_extension_update_paths('extension_name>');
CREATE TYPE pair AS ( k text, v text );
-CREATE OR REPLACE FUNCTION pair(text, text)
+CREATE FUNCTION pair(text, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@extschema@.pair;';
CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);
-- "SET search_path" is easy to get right, but qualified names perform better.
-CREATE OR REPLACE FUNCTION lower(pair)
+CREATE FUNCTION lower(pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW(lower($1.k), lower($1.v))::@extschema@.pair;'
SET search_path = pg_temp;
-CREATE OR REPLACE FUNCTION pair_concat(pair, pair)
+CREATE FUNCTION pair_concat(pair, pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
$1.v OPERATOR(pg_catalog.||) $2.v)::@extschema@.pair;';
@@ -925,6 +1042,7 @@ AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
# pair extension
comment = 'A key/value pair data type'
default_version = '1.0'
+# cannot be relocatable because of use of @extschema@
relocatable = false
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index 5c411950dd..d9f92e15a3 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -633,6 +633,15 @@ ALTER TABLE tablename ALTER hstorecol TYPE hstore USING hstorecol || '';
convention). If you use them, hstore values are mapped to
Python dictionaries.
+
+
+
+ It is strongly recommended that the transform extensions be installed in
+ the same schema as hstore. Otherwise there are
+ installation-time security hazards if a transform extension's schema
+ contains objects defined by a hostile user.
+
+
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index b399391da1..70ad7d3421 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -674,6 +674,15 @@ ltreetest=> SELECT ins_label(path,2,'Space') FROM test WHERE path <@ 'Top.
creating a function, ltree values are mapped to Python lists.
(The reverse is currently not supported, however.)
+
+
+
+ It is strongly recommended that the transform extensions be installed in
+ the same schema as ltree. Otherwise there are
+ installation-time security hazards if a transform extension's schema
+ contains objects defined by a hostile user.
+
+
diff --git a/doc/src/sgml/ref/create_extension.sgml b/doc/src/sgml/ref/create_extension.sgml
index a1e7e4f812..b96602a7dd 100644
--- a/doc/src/sgml/ref/create_extension.sgml
+++ b/doc/src/sgml/ref/create_extension.sgml
@@ -161,6 +161,33 @@ CREATE EXTENSION [ IF NOT EXISTS ] extension_name
system views.
+
+
+ Installing an extension as superuser requires trusting that the
+ extension's author wrote the extension installation script in a secure
+ fashion. It is not terribly difficult for a malicious user to create
+ trojan-horse objects that will compromise later execution of a
+ carelessly-written extension script, allowing that user to acquire
+ superuser privileges. However, trojan-horse objects are only hazardous
+ if they are in the search_path during script
+ execution, meaning that they are in the extension's installation target
+ schema or in the schema of some extension it depends on. Therefore, a
+ good rule of thumb when dealing with extensions whose scripts have not
+ been carefully vetted is to install them only into schemas for which
+ CREATE privilege has not been and will not be granted to any untrusted
+ users. Likewise for any extensions they depend on.
+
+
+
+ The extensions supplied with PostgreSQL are
+ believed to be secure against installation-time attacks of this sort,
+ except for a few that depend on other extensions. As stated in the
+ documentation for those extensions, they should be installed into secure
+ schemas, or installed into the same schemas as the extensions they
+ depend on, or both.
+
+
+
For information about writing new extensions, see
.
@@ -172,8 +199,13 @@ CREATE EXTENSION [ IF NOT EXISTS ] extension_name
Install the hstore extension into the
- current database:
+ current database, placing its objects in schema addons:
+CREATE EXTENSION hstore SCHEMA addons;
+
+ Another way to accomplish the same thing:
+
+SET search_path = addons;
CREATE EXTENSION hstore;
diff --git a/doc/src/sgml/release-9.5.sgml b/doc/src/sgml/release-9.5.sgml
index 853febf9a3..95213333a2 100644
--- a/doc/src/sgml/release-9.5.sgml
+++ b/doc/src/sgml/release-9.5.sgml
@@ -6856,7 +6856,7 @@ Branch: REL9_5_STABLE [3c0e07a46] 2018-05-01 12:02:41 -0400
one's search path. Relevant documentation appears in
(for database administrators and users),
(for application authors),
- (for extension authors), and
+ (for extension authors), and
(for authors
of SECURITY DEFINER functions).
(CVE-2018-1058)
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 39c9411367..a4ab97cf5c 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -821,9 +821,21 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
GUC_ACTION_SAVE, true, 0, false);
/*
- * Set up the search path to contain the target schema, then the schemas
- * of any prerequisite extensions, and nothing else. In particular this
- * makes the target schema be the default creation target namespace.
+ * Similarly disable check_function_bodies, to ensure that SQL functions
+ * won't be parsed during creation.
+ */
+ if (check_function_bodies)
+ (void) set_config_option("check_function_bodies", "off",
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_SAVE, true, 0, false);
+
+ /*
+ * Set up the search path to have the target schema first, making it be
+ * the default creation target namespace. Then add the schemas of any
+ * prerequisite extensions, unless they are in pg_catalog which would be
+ * searched anyway. (Listing pg_catalog explicitly in a non-first
+ * position would be bad for security.) Finally add pg_temp to ensure
+ * that temp objects can't take precedence over others.
*
* Note: it might look tempting to use PushOverrideSearchPath for this,
* but we cannot do that. We have to actually set the search_path GUC in
@@ -837,9 +849,10 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
Oid reqschema = lfirst_oid(lc);
char *reqname = get_namespace_name(reqschema);
- if (reqname)
+ if (reqname && strcmp(reqname, "pg_catalog") != 0)
appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
}
+ appendStringInfoString(&pathbuf, ", pg_temp");
(void) set_config_option("search_path", pathbuf.data,
PGC_USERSET, PGC_S_SESSION,
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 2257c56256..3a19db583b 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -249,6 +249,8 @@ DefineOperator(List *names, List *parameters)
*/
if (joinName)
{
+ Oid joinOid2;
+
typeId[0] = INTERNALOID; /* PlannerInfo */
typeId[1] = OIDOID; /* operator OID */
typeId[2] = INTERNALOID; /* args list */
@@ -257,15 +259,26 @@ DefineOperator(List *names, List *parameters)
/*
* As of Postgres 8.4, the preferred signature for join estimators has
- * 5 arguments, but we still allow the old 4-argument form. Try the
- * preferred form first.
+ * 5 arguments, but we still allow the old 4-argument form. Whine
+ * about ambiguity if both forms exist.
*/
joinOid = LookupFuncName(joinName, 5, typeId, true);
- if (!OidIsValid(joinOid))
- joinOid = LookupFuncName(joinName, 4, typeId, true);
- /* If not found, reference the 5-argument signature in error msg */
- if (!OidIsValid(joinOid))
- joinOid = LookupFuncName(joinName, 5, typeId, false);
+ joinOid2 = LookupFuncName(joinName, 4, typeId, true);
+ if (OidIsValid(joinOid))
+ {
+ if (OidIsValid(joinOid2))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("join estimator function %s has multiple matches",
+ NameListToString(joinName))));
+ }
+ else
+ {
+ joinOid = joinOid2;
+ /* If not found, reference the 5-argument signature in error msg */
+ if (!OidIsValid(joinOid))
+ joinOid = LookupFuncName(joinName, 5, typeId, false);
+ }
/* estimators must return float8 */
if (get_func_rettype(joinOid) != FLOAT8OID)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index cedd42433d..b5875405f0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1642,7 +1642,11 @@ static Oid
findTypeInputFunction(List *procname, Oid typeOid)
{
Oid argList[3];
+ int nmatches = 0;
Oid procOid;
+ Oid procOid2;
+ Oid procOid3;
+ Oid procOid4;
/*
* Input functions can take a single argument of type CSTRING, or three
@@ -1650,32 +1654,45 @@ findTypeInputFunction(List *procname, Oid typeOid)
*
* For backwards compatibility we allow OPAQUE in place of CSTRING; if we
* see this, we issue a warning and fix up the pg_proc entry.
+ *
+ * Whine about ambiguity if multiple forms exist.
*/
argList[0] = CSTRINGOID;
-
- procOid = LookupFuncName(procname, 1, argList, true);
- if (OidIsValid(procOid))
- return procOid;
-
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, true);
if (OidIsValid(procOid))
- return procOid;
+ nmatches++;
+ procOid2 = LookupFuncName(procname, 3, argList, true);
+ if (OidIsValid(procOid2))
+ nmatches++;
- /* No luck, try it with OPAQUE */
argList[0] = OPAQUEOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid3 = LookupFuncName(procname, 1, argList, true);
+ if (OidIsValid(procOid3))
+ nmatches++;
+ procOid4 = LookupFuncName(procname, 3, argList, true);
+ if (OidIsValid(procOid4))
+ nmatches++;
- if (!OidIsValid(procOid))
- {
- argList[1] = OIDOID;
- argList[2] = INT4OID;
+ if (nmatches > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("type input function %s has multiple matches",
+ NameListToString(procname))));
- procOid = LookupFuncName(procname, 3, argList, true);
- }
+ if (OidIsValid(procOid))
+ return procOid;
+ if (OidIsValid(procOid2))
+ return procOid2;
+
+ /* Cases with OPAQUE need adjustment */
+ if (OidIsValid(procOid3))
+ procOid = procOid3;
+ else
+ procOid = procOid4;
if (OidIsValid(procOid))
{
@@ -1761,24 +1778,32 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
{
Oid argList[3];
Oid procOid;
+ Oid procOid2;
/*
* Receive functions can take a single argument of type INTERNAL, or three
- * arguments (internal, typioparam OID, typmod).
+ * arguments (internal, typioparam OID, typmod). Whine about ambiguity if
+ * both forms exist.
*/
argList[0] = INTERNALOID;
-
- procOid = LookupFuncName(procname, 1, argList, true);
- if (OidIsValid(procOid))
- return procOid;
-
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, true);
+ procOid2 = LookupFuncName(procname, 3, argList, true);
if (OidIsValid(procOid))
+ {
+ if (OidIsValid(procOid2))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("type receive function %s has multiple matches",
+ NameListToString(procname))));
return procOid;
+ }
+ else if (OidIsValid(procOid2))
+ return procOid2;
+ /* If not found, reference the 1-argument signature in error msg */
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",