Commit 5770172c authored by Noah Misch's avatar Noah Misch

Document security implications of search_path and the public schema.

The ability to create like-named objects in different schemas opens up
the potential for users to change the behavior of other users' queries,
maliciously or accidentally.  When you connect to a PostgreSQL server,
you should remove from your search_path any schema for which a user
other than yourself or superusers holds the CREATE privilege.  If you do
not, other users holding CREATE privilege can redefine the behavior of
your commands, causing them to perform arbitrary SQL statements under
your identity.  "SET search_path = ..." and "SELECT
pg_catalog.set_config(...)" are not vulnerable to such hijacking, so one
can use either as the first command of a session.  As special
exceptions, the following client applications behave as documented
regardless of search_path settings and schema privileges: clusterdb
createdb createlang createuser dropdb droplang dropuser ecpg (not
programs it generates) initdb oid2name pg_archivecleanup pg_basebackup
pg_config pg_controldata pg_ctl pg_dump pg_dumpall pg_isready
pg_receivewal pg_recvlogical pg_resetwal pg_restore pg_rewind pg_standby
pg_test_fsync pg_test_timing pg_upgrade pg_waldump reindexdb vacuumdb
vacuumlo.  Not included are core client programs that run user-specified
SQL commands, namely psql and pgbench.  PostgreSQL encourages non-core
client applications to do likewise.

Document this in the context of libpq connections, psql connections,
dblink connections, ECPG connections, extension packaging, and schema
usage patterns.  The principal defense for applications is "SELECT
pg_catalog.set_config('search_path', '', false)", and the principal
defense for databases is "REVOKE CREATE ON SCHEMA public FROM PUBLIC".
Either one is sufficient to prevent attack.  After a REVOKE, consider
auditing the public schema for objects named like pg_catalog objects.

Authors of SECURITY DEFINER functions use some of the same defenses, and
the CREATE FUNCTION reference page already covered them thoroughly.
This is a good opportunity to audit SECURITY DEFINER functions for
robust security practice.

Back-patch to 9.3 (all supported versions).

Reviewed by Michael Paquier and Jonathan S. Katz.  Reported by Arseniy
Sharoglazov.

Security: CVE-2018-1058
parent 582edc36
...@@ -6329,6 +6329,13 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; ...@@ -6329,6 +6329,13 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
setting, either globally or per-user. setting, either globally or per-user.
</para> </para>
<para>
For more information on schema handling, see
<xref linkend="ddl-schemas"/>. In particular, the default
configuration is suitable only when the database has a single user or
a few mutually-trusting users.
</para>
<para> <para>
The current effective value of the search path can be examined The current effective value of the search path can be examined
via the <acronym>SQL</acronym> function via the <acronym>SQL</acronym> function
...@@ -6340,9 +6347,6 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; ...@@ -6340,9 +6347,6 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
appearing in <varname>search_path</varname> were resolved. appearing in <varname>search_path</varname> were resolved.
</para> </para>
<para>
For more information on schema handling, see <xref linkend="ddl-schemas"/>.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -75,7 +75,7 @@ CREATE EXTENSION <replaceable>module_name</replaceable>; ...@@ -75,7 +75,7 @@ CREATE EXTENSION <replaceable>module_name</replaceable>;
choice. To do that, add <literal>SCHEMA choice. To do that, add <literal>SCHEMA
<replaceable>schema_name</replaceable></literal> to the <command>CREATE EXTENSION</command> <replaceable>schema_name</replaceable></literal> to the <command>CREATE EXTENSION</command>
command. By default, the objects will be placed in your current creation command. By default, the objects will be placed in your current creation
target schema, typically <literal>public</literal>. target schema, which in turn defaults to <literal>public</literal>.
</para> </para>
<para> <para>
......
...@@ -83,7 +83,7 @@ dblink_connect(text connname, text connstr) returns text ...@@ -83,7 +83,7 @@ dblink_connect(text connname, text connstr) returns text
<listitem> <listitem>
<para><application>libpq</application>-style connection info string, for example <para><application>libpq</application>-style connection info string, for example
<literal>hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres <literal>hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres
password=mypasswd</literal>. password=mypasswd options=-csearch_path=</literal>.
For details see <xref linkend="libpq-connstring"/>. For details see <xref linkend="libpq-connstring"/>.
Alternatively, the name of a foreign server. Alternatively, the name of a foreign server.
</para> </para>
...@@ -104,6 +104,17 @@ dblink_connect(text connname, text connstr) returns text ...@@ -104,6 +104,17 @@ dblink_connect(text connname, text connstr) returns text
<refsect1> <refsect1>
<title>Notes</title> <title>Notes</title>
<para>
If untrusted users have access to a database that has not adopted a
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
begin each session by removing publicly-writable schemas from
<varname>search_path</varname>. One could, for example,
add <literal>options=-csearch_path=</literal> to
<parameter>connstr</parameter>. This consideration is not specific
to <filename>dblink</filename>; it applies to every interface for
executing arbitrary SQL commands.
</para>
<para> <para>
Only superusers may use <function>dblink_connect</function> to create Only superusers may use <function>dblink_connect</function> to create
non-password-authenticated connections. If non-superusers need this non-password-authenticated connections. If non-superusers need this
...@@ -121,13 +132,13 @@ dblink_connect(text connname, text connstr) returns text ...@@ -121,13 +132,13 @@ dblink_connect(text connname, text connstr) returns text
<title>Examples</title> <title>Examples</title>
<screen> <screen>
SELECT dblink_connect('dbname=postgres'); SELECT dblink_connect('dbname=postgres options=-csearch_path=');
dblink_connect dblink_connect
---------------- ----------------
OK OK
(1 row) (1 row)
SELECT dblink_connect('myconn', 'dbname=postgres'); SELECT dblink_connect('myconn', 'dbname=postgres options=-csearch_path=');
dblink_connect dblink_connect
---------------- ----------------
OK OK
...@@ -416,7 +427,8 @@ dblink(text sql [, bool fail_on_error]) returns setof record ...@@ -416,7 +427,8 @@ dblink(text sql [, bool fail_on_error]) returns setof record
<programlisting> <programlisting>
SELECT * SELECT *
FROM dblink('dbname=mydb', 'select proname, prosrc from pg_proc') FROM dblink('dbname=mydb options=-csearch_path=',
'select proname, prosrc from pg_proc')
AS t1(proname name, prosrc text) AS t1(proname name, prosrc text)
WHERE proname LIKE 'bytea%'; WHERE proname LIKE 'bytea%';
</programlisting> </programlisting>
...@@ -450,7 +462,8 @@ SELECT * ...@@ -450,7 +462,8 @@ SELECT *
<programlisting> <programlisting>
CREATE VIEW myremote_pg_proc AS CREATE VIEW myremote_pg_proc AS
SELECT * SELECT *
FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc') FROM dblink('dbname=postgres options=-csearch_path=',
'select proname, prosrc from pg_proc')
AS t1(proname name, prosrc text); AS t1(proname name, prosrc text);
SELECT * FROM myremote_pg_proc WHERE proname LIKE 'bytea%'; SELECT * FROM myremote_pg_proc WHERE proname LIKE 'bytea%';
...@@ -461,7 +474,8 @@ SELECT * FROM myremote_pg_proc WHERE proname LIKE 'bytea%'; ...@@ -461,7 +474,8 @@ SELECT * FROM myremote_pg_proc WHERE proname LIKE 'bytea%';
<title>Examples</title> <title>Examples</title>
<screen> <screen>
SELECT * FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc') SELECT * FROM dblink('dbname=postgres options=-csearch_path=',
'select proname, prosrc from pg_proc')
AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%'; AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%';
proname | prosrc proname | prosrc
------------+------------ ------------+------------
...@@ -479,7 +493,7 @@ SELECT * FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc') ...@@ -479,7 +493,7 @@ SELECT * FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc')
byteaout | byteaout byteaout | byteaout
(12 rows) (12 rows)
SELECT dblink_connect('dbname=postgres'); SELECT dblink_connect('dbname=postgres options=-csearch_path=');
dblink_connect dblink_connect
---------------- ----------------
OK OK
...@@ -503,7 +517,7 @@ SELECT * FROM dblink('select proname, prosrc from pg_proc') ...@@ -503,7 +517,7 @@ SELECT * FROM dblink('select proname, prosrc from pg_proc')
byteaout | byteaout byteaout | byteaout
(12 rows) (12 rows)
SELECT dblink_connect('myconn', 'dbname=regression'); SELECT dblink_connect('myconn', 'dbname=regression options=-csearch_path=');
dblink_connect dblink_connect
---------------- ----------------
OK OK
...@@ -778,7 +792,7 @@ dblink_open(text connname, text cursorname, text sql [, bool fail_on_error]) ret ...@@ -778,7 +792,7 @@ dblink_open(text connname, text cursorname, text sql [, bool fail_on_error]) ret
<title>Examples</title> <title>Examples</title>
<screen> <screen>
SELECT dblink_connect('dbname=postgres'); SELECT dblink_connect('dbname=postgres options=-csearch_path=');
dblink_connect dblink_connect
---------------- ----------------
OK OK
...@@ -899,7 +913,7 @@ dblink_fetch(text connname, text cursorname, int howmany [, bool fail_on_error]) ...@@ -899,7 +913,7 @@ dblink_fetch(text connname, text cursorname, int howmany [, bool fail_on_error])
<title>Examples</title> <title>Examples</title>
<screen> <screen>
SELECT dblink_connect('dbname=postgres'); SELECT dblink_connect('dbname=postgres options=-csearch_path=');
dblink_connect dblink_connect
---------------- ----------------
OK OK
...@@ -1036,7 +1050,7 @@ dblink_close(text connname, text cursorname [, bool fail_on_error]) returns text ...@@ -1036,7 +1050,7 @@ dblink_close(text connname, text cursorname [, bool fail_on_error]) returns text
<title>Examples</title> <title>Examples</title>
<screen> <screen>
SELECT dblink_connect('dbname=postgres'); SELECT dblink_connect('dbname=postgres options=-csearch_path=');
dblink_connect dblink_connect
---------------- ----------------
OK OK
......
...@@ -2172,6 +2172,20 @@ CREATE TABLE public.products ( ... ); ...@@ -2172,6 +2172,20 @@ CREATE TABLE public.products ( ... );
in other schemas in the database. in other schemas in the database.
</para> </para>
<para>
The ability to create like-named objects in different schemas complicates
writing a query that references precisely the same objects every time. It
also opens up the potential for users to change the behavior of other
users' queries, maliciously or accidentally. Due to the prevalence of
unqualified names in queries and their use
in <productname>PostgreSQL</productname> internals, adding a schema
to <varname>search_path</varname> effectively trusts all users having
<literal>CREATE</literal> privilege on that schema. When you run an
ordinary query, a malicious user able to create objects in a schema of
your search path can take control and execute arbitrary SQL functions as
though you executed them.
</para>
<indexterm> <indexterm>
<primary>schema</primary> <primary>schema</primary>
<secondary>current</secondary> <secondary>current</secondary>
...@@ -2288,8 +2302,9 @@ SELECT 3 OPERATOR(pg_catalog.+) 4; ...@@ -2288,8 +2302,9 @@ SELECT 3 OPERATOR(pg_catalog.+) 4;
the schema the schema
<literal>public</literal>. This allows all users that are able to <literal>public</literal>. This allows all users that are able to
connect to a given database to create objects in its connect to a given database to create objects in its
<literal>public</literal> schema. If you do <literal>public</literal> schema.
not want to allow that, you can revoke that privilege: Some <link linkend="ddl-schemas-patterns">usage patterns</link> call for
revoking that privilege:
<programlisting> <programlisting>
REVOKE CREATE ON SCHEMA public FROM PUBLIC; REVOKE CREATE ON SCHEMA public FROM PUBLIC;
</programlisting> </programlisting>
...@@ -2339,50 +2354,75 @@ REVOKE CREATE ON SCHEMA public FROM PUBLIC; ...@@ -2339,50 +2354,75 @@ REVOKE CREATE ON SCHEMA public FROM PUBLIC;
<title>Usage Patterns</title> <title>Usage Patterns</title>
<para> <para>
Schemas can be used to organize your data in many ways. There are Schemas can be used to organize your data in many ways. There are a few
a few usage patterns that are recommended and are easily supported by usage patterns easily supported by the default configuration, only one of
the default configuration: which suffices when database users mistrust other database users:
<itemizedlist> <itemizedlist>
<listitem> <listitem>
<!-- "DROP SCHEMA public" is inferior to this REVOKE, because pg_dump
doesn't preserve that DROP. -->
<para> <para>
If you do not create any schemas then all users access the Constrain ordinary users to user-private schemas. To implement this,
public schema implicitly. This simulates the situation where issue <literal>REVOKE CREATE ON SCHEMA public FROM PUBLIC</literal>,
schemas are not available at all. This setup is mainly and create a schema for each user with the same name as that user. If
recommended when there is only a single user or a few cooperating affected users had logged in before this, consider auditing the public
users in a database. This setup also allows smooth transition schema for objects named like objects in
from the non-schema-aware world. schema <literal>pg_catalog</literal>. Recall that the default search
path starts with <literal>$user</literal>, which resolves to the user
name. Therefore, if each user has a separate schema, they access their
own schemas by default.
</para> </para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>
You can create a schema for each user with the same name as Remove the public schema from each user's default search path
that user. Recall that the default search path starts with using <literal>ALTER ROLE <replaceable>user</replaceable> SET
<literal>$user</literal>, which resolves to the user name. search_path = "$user"</literal>. Everyone retains the ability to
Therefore, if each user has a separate schema, they access their create objects in the public schema, but only qualified names will
own schemas by default. choose those objects. A user holding the <literal>CREATEROLE</literal>
privilege can undo this setting and issue arbitrary queries under the
identity of users relying on the setting. If you
grant <literal>CREATEROLE</literal> to users not warranting this
almost-superuser ability, use the first pattern instead.
</para> </para>
</listitem>
<listitem>
<para> <para>
If you use this setup then you might also want to revoke access Remove the public schema from <varname>search_path</varname> in
to the public schema (or drop it altogether), so users are <link linkend="config-setting-configuration-file"><filename>postgresql.conf</filename></link>.
truly constrained to their own schemas. The ensuing user experience matches the previous pattern. In addition
to that pattern's implications for <literal>CREATEROLE</literal>, this
trusts database owners the same way. If you assign
the <literal>CREATEROLE</literal>
privilege, <literal>CREATEDB</literal> privilege or individual database
ownership to users not warranting almost-superuser access, use the
first pattern instead.
</para> </para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>
To install shared applications (tables to be used by everyone, Keep the default. All users access the public schema implicitly. This
additional functions provided by third parties, etc.), put them simulates the situation where schemas are not available at all, giving
into separate schemas. Remember to grant appropriate a smooth transition from the non-schema-aware world. However, any user
privileges to allow the other users to access them. Users can can issue arbitrary queries under the identity of any user not electing
then refer to these additional objects by qualifying the names to protect itself individually. This pattern is acceptable only when
with a schema name, or they can put the additional schemas into the database has a single user or a few mutually-trusting users.
their search path, as they choose.
</para> </para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
</para> </para>
<para>
For any pattern, to install shared applications (tables to be used by
everyone, additional functions provided by third parties, etc.), put them
into separate schemas. Remember to grant appropriate privileges to allow
the other users to access them. Users can then refer to these additional
objects by qualifying the names with a schema name, or they can put the
additional schemas into their search path, as they choose.
</para>
</sect2> </sect2>
<sect2 id="ddl-schemas-portability"> <sect2 id="ddl-schemas-portability">
...@@ -2405,7 +2445,7 @@ REVOKE CREATE ON SCHEMA public FROM PUBLIC; ...@@ -2405,7 +2445,7 @@ REVOKE CREATE ON SCHEMA public FROM PUBLIC;
<para> <para>
Also, there is no concept of a <literal>public</literal> schema in the Also, there is no concept of a <literal>public</literal> schema in the
SQL standard. For maximum conformance to the standard, you should SQL standard. For maximum conformance to the standard, you should
not use (perhaps even remove) the <literal>public</literal> schema. not use the <literal>public</literal> schema.
</para> </para>
<para> <para>
......
...@@ -186,6 +186,18 @@ EXEC SQL CONNECT TO <replaceable>target</replaceable> <optional>AS <replaceable> ...@@ -186,6 +186,18 @@ EXEC SQL CONNECT TO <replaceable>target</replaceable> <optional>AS <replaceable>
chapter). chapter).
</para> </para>
<para>
If untrusted users have access to a database that has not adopted a
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
begin each session by removing publicly-writable schemas
from <varname>search_path</varname>. For example,
add <literal>options=-csearch_path=</literal>
to <literal><replaceable>options</replaceable></literal>, or
issue <literal>EXEC SQL SELECT pg_catalog.set_config('search_path', '',
false);</literal> after connecting. This consideration is not specific to
ECPG; it applies to every interface for executing arbitrary SQL commands.
</para>
<para> <para>
Here are some examples of <command>CONNECT</command> statements: Here are some examples of <command>CONNECT</command> statements:
<programlisting> <programlisting>
...@@ -266,8 +278,11 @@ int ...@@ -266,8 +278,11 @@ int
main() main()
{ {
EXEC SQL CONNECT TO testdb1 AS con1 USER testuser; EXEC SQL CONNECT TO testdb1 AS con1 USER testuser;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
EXEC SQL CONNECT TO testdb2 AS con2 USER testuser; EXEC SQL CONNECT TO testdb2 AS con2 USER testuser;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
EXEC SQL CONNECT TO testdb3 AS con3 USER testuser; EXEC SQL CONNECT TO testdb3 AS con3 USER testuser;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
/* This query would be executed in the last opened database "testdb3". */ /* This query would be executed in the last opened database "testdb3". */
EXEC SQL SELECT current_database() INTO :dbname; EXEC SQL SELECT current_database() INTO :dbname;
...@@ -1093,6 +1108,7 @@ EXEC SQL BEGIN DECLARE SECTION; ...@@ -1093,6 +1108,7 @@ EXEC SQL BEGIN DECLARE SECTION;
EXEC SQL END DECLARE SECTION; EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO testdb; EXEC SQL CONNECT TO testdb;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
in = PGTYPESinterval_new(); in = PGTYPESinterval_new();
EXEC SQL SELECT '1 min'::interval INTO :in; EXEC SQL SELECT '1 min'::interval INTO :in;
...@@ -1147,6 +1163,7 @@ EXEC SQL BEGIN DECLARE SECTION; ...@@ -1147,6 +1163,7 @@ EXEC SQL BEGIN DECLARE SECTION;
EXEC SQL END DECLARE SECTION; EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO testdb; EXEC SQL CONNECT TO testdb;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
num = PGTYPESnumeric_new(); num = PGTYPESnumeric_new();
dec = PGTYPESdecimal_new(); dec = PGTYPESdecimal_new();
...@@ -1221,6 +1238,7 @@ EXEC SQL END DECLARE SECTION; ...@@ -1221,6 +1238,7 @@ EXEC SQL END DECLARE SECTION;
memset(dbid, 0, sizeof(int) * 8); memset(dbid, 0, sizeof(int) * 8);
EXEC SQL CONNECT TO testdb; EXEC SQL CONNECT TO testdb;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
/* Retrieve multiple rows into arrays at once. */ /* Retrieve multiple rows into arrays at once. */
EXEC SQL SELECT oid,datname INTO :dbid, :dbname FROM pg_database; EXEC SQL SELECT oid,datname INTO :dbid, :dbname FROM pg_database;
...@@ -1887,6 +1905,7 @@ char *stmt = "SELECT u.usename as dbaname, d.datname " ...@@ -1887,6 +1905,7 @@ char *stmt = "SELECT u.usename as dbaname, d.datname "
EXEC SQL END DECLARE SECTION; EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO testdb AS con1 USER testuser; EXEC SQL CONNECT TO testdb AS con1 USER testuser;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
EXEC SQL PREPARE stmt1 FROM :stmt; EXEC SQL PREPARE stmt1 FROM :stmt;
...@@ -4317,6 +4336,7 @@ main(void) ...@@ -4317,6 +4336,7 @@ main(void)
EXEC SQL END DECLARE SECTION; EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO testdb AS con1 USER testuser; EXEC SQL CONNECT TO testdb AS con1 USER testuser;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
EXEC SQL PREPARE stmt1 FROM :query; EXEC SQL PREPARE stmt1 FROM :query;
EXEC SQL DECLARE cur1 CURSOR FOR stmt1; EXEC SQL DECLARE cur1 CURSOR FOR stmt1;
...@@ -4478,6 +4498,7 @@ main(void) ...@@ -4478,6 +4498,7 @@ main(void)
EXEC SQL END DECLARE SECTION; EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO uptimedb AS con1 USER uptime; EXEC SQL CONNECT TO uptimedb AS con1 USER uptime;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
EXEC SQL PREPARE stmt1 FROM :query; EXEC SQL PREPARE stmt1 FROM :query;
EXEC SQL DECLARE cur1 CURSOR FOR stmt1; EXEC SQL DECLARE cur1 CURSOR FOR stmt1;
...@@ -5909,6 +5930,7 @@ main(void) ...@@ -5909,6 +5930,7 @@ main(void)
memset(buf, 1, buflen); memset(buf, 1, buflen);
EXEC SQL CONNECT TO testdb AS con1; EXEC SQL CONNECT TO testdb AS con1;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
conn = ECPGget_PGconn("con1"); conn = ECPGget_PGconn("con1");
printf("conn = %p\n", conn); printf("conn = %p\n", conn);
...@@ -6038,6 +6060,7 @@ class TestCpp ...@@ -6038,6 +6060,7 @@ class TestCpp
TestCpp::TestCpp() TestCpp::TestCpp()
{ {
EXEC SQL CONNECT TO testdb1; EXEC SQL CONNECT TO testdb1;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
} }
void Test::test() void Test::test()
...@@ -6117,6 +6140,7 @@ void ...@@ -6117,6 +6140,7 @@ void
db_connect() db_connect()
{ {
EXEC SQL CONNECT TO testdb1; EXEC SQL CONNECT TO testdb1;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
} }
void void
...@@ -6510,12 +6534,14 @@ EXEC SQL END DECLARE SECTION; ...@@ -6510,12 +6534,14 @@ EXEC SQL END DECLARE SECTION;
ECPGdebug(1, stderr); ECPGdebug(1, stderr);
EXEC SQL CONNECT TO :dbname USER :user; EXEC SQL CONNECT TO :dbname USER :user;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
EXEC SQL SELECT version() INTO :ver; EXEC SQL SELECT version() INTO :ver;
EXEC SQL DISCONNECT; EXEC SQL DISCONNECT;
printf("version: %s\n", ver); printf("version: %s\n", ver);
EXEC SQL CONNECT TO :connection USER :user; EXEC SQL CONNECT TO :connection USER :user;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
EXEC SQL SELECT version() INTO :ver; EXEC SQL SELECT version() INTO :ver;
EXEC SQL DISCONNECT; EXEC SQL DISCONNECT;
...@@ -7116,6 +7142,7 @@ EXEC SQL BEGIN DECLARE SECTION; ...@@ -7116,6 +7142,7 @@ EXEC SQL BEGIN DECLARE SECTION;
EXEC SQL END DECLARE SECTION; EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO testdb AS con1 USER testuser; EXEC SQL CONNECT TO testdb AS con1 USER testuser;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
EXEC SQL ALLOCATE DESCRIPTOR d; EXEC SQL ALLOCATE DESCRIPTOR d;
/* Declare, open a cursor, and assign a descriptor to the cursor */ /* Declare, open a cursor, and assign a descriptor to the cursor */
...@@ -7673,6 +7700,7 @@ EXEC SQL BEGIN DECLARE SECTION; ...@@ -7673,6 +7700,7 @@ EXEC SQL BEGIN DECLARE SECTION;
EXEC SQL END DECLARE SECTION; EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO testdb AS con1; EXEC SQL CONNECT TO testdb AS con1;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
EXEC SQL SELECT current_database(), 256 INTO :t:t_ind LIMIT 1; EXEC SQL SELECT current_database(), 256 INTO :t:t_ind LIMIT 1;
...@@ -7829,6 +7857,7 @@ int ...@@ -7829,6 +7857,7 @@ int
main(void) main(void)
{ {
EXEC SQL CONNECT TO testdb AS con1; EXEC SQL CONNECT TO testdb AS con1;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
EXEC SQL ALLOCATE DESCRIPTOR d; EXEC SQL ALLOCATE DESCRIPTOR d;
EXEC SQL DECLARE cur CURSOR FOR SELECT current_database(), 'hoge', 256; EXEC SQL DECLARE cur CURSOR FOR SELECT current_database(), 'hoge', 256;
EXEC SQL OPEN cur; EXEC SQL OPEN cur;
......
...@@ -430,6 +430,32 @@ ...@@ -430,6 +430,32 @@
dropping the whole extension. dropping the whole extension.
</para> </para>
<sect2 id="extend-extensions-style">
<title>Defining Extension Objects</title>
<!-- XXX It's not enough to use qualified names, because one might write a
qualified name to an object that itself uses unqualified names. Many
information_schema functions have that defect, for example. However,
that's a defect in the referenced object, and relatively few queries
will be affected. Also, we direct applications to secure search_path
when connecting to an untrusted database; if applications do that,
they are immune to known attacks even if some extension refers to a
defective object. Therefore, guide extension authors as though core
PostgreSQL contained no such defect. -->
<para>
Widely-distributed extensions should assume little about the database
they occupy. In particular, unless you issued <literal>SET search_path =
pg_temp</literal>, assume each unqualified name could resolve to an
object that a malicious user has defined. Beware of constructs that
depend on <varname>search_path</varname> implicitly: <token>IN</token>
and <literal>CASE <replaceable>expression</replaceable> WHEN</literal>
always select an operator using the search path. In their place, use
<literal>OPERATOR(<replaceable>schema</replaceable>.=) ANY</literal>
and <literal>CASE WHEN <replaceable>expression</replaceable></literal>.
</para>
</sect2>
<sect2> <sect2>
<title>Extension Files</title> <title>Extension Files</title>
...@@ -984,24 +1010,24 @@ SELECT * FROM pg_extension_update_paths('<replaceable>extension_name</replaceabl ...@@ -984,24 +1010,24 @@ SELECT * FROM pg_extension_update_paths('<replaceable>extension_name</replaceabl
-- complain if script is sourced in psql, rather than via CREATE EXTENSION -- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION pair" to load this file. \quit \echo Use "CREATE EXTENSION pair" to load this file. \quit
CREATE TYPE pair AS ( k text, v text ); CREATE TYPE pair AS ( k pg_catalog.text, v pg_catalog.text );
CREATE OR REPLACE FUNCTION pair(anyelement, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
CREATE OR REPLACE FUNCTION pair(text, anyelement) CREATE OR REPLACE FUNCTION pair(pg_catalog.text, pg_catalog.text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair'; RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@extschema@.pair;';
CREATE OR REPLACE FUNCTION pair(anyelement, anyelement) CREATE OPERATOR ~> (LEFTARG = pg_catalog.text,
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair'; RIGHTARG = pg_catalog.text, PROCEDURE = pair);
CREATE OR REPLACE FUNCTION pair(text, text) -- "SET search_path" is easy to get right, but qualified names perform better.
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;'; CREATE OR REPLACE FUNCTION lower(pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW(lower($1.k), lower($1.v))::@extschema@.pair;'
SET search_path = pg_temp;
CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = anyelement, PROCEDURE = pair); CREATE OR REPLACE FUNCTION pair_concat(pair, pair)
CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = text, PROCEDURE = pair); RETURNS pair LANGUAGE SQL
CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = anyelement, PROCEDURE = pair); AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair); $1.v OPERATOR(pg_catalog.||) $2.v)::@extschema@.pair;';
]]> ]]>
</programlisting> </programlisting>
</para> </para>
...@@ -1013,7 +1039,7 @@ CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair); ...@@ -1013,7 +1039,7 @@ CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);
# pair extension # pair extension
comment = 'A key/value pair data type' comment = 'A key/value pair data type'
default_version = '1.0' default_version = '1.0'
relocatable = true relocatable = false
</programlisting> </programlisting>
</para> </para>
......
...@@ -65,6 +65,22 @@ ...@@ -65,6 +65,22 @@
the return value for a successful connection before queries are sent the return value for a successful connection before queries are sent
via the connection object. via the connection object.
<warning>
<para>
If untrusted users have access to a database that has not adopted a
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
begin each session by removing publicly-writable schemas from
<varname>search_path</varname>. One can set parameter key
word <literal>options</literal> to
value <literal>-csearch_path=</literal>. Alternately, one can
issue <literal>PQexec(<replaceable>conn</replaceable>, "SELECT
pg_catalog.set_config('search_path', '', false)")</literal> after
connecting. This consideration is not specific
to <application>libpq</application>; it applies to every interface for
executing arbitrary SQL commands.
</para>
</warning>
<warning> <warning>
<para> <para>
On Unix, forking a process with open libpq connections can lead to On Unix, forking a process with open libpq connections can lead to
...@@ -6878,7 +6894,8 @@ main(void) ...@@ -6878,7 +6894,8 @@ main(void)
{ {
mydata *data; mydata *data;
PGresult *res; PGresult *res;
PGconn *conn = PQconnectdb("dbname = postgres"); PGconn *conn =
PQconnectdb("dbname=postgres options=-csearch_path=");
if (PQstatus(conn) != CONNECTION_OK) if (PQstatus(conn) != CONNECTION_OK)
{ {
...@@ -8305,6 +8322,22 @@ main(int argc, char **argv) ...@@ -8305,6 +8322,22 @@ main(int argc, char **argv)
exit_nicely(conn); exit_nicely(conn);
} }
/* Set always-secure search path, so malicous users can't take control. */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/*
* Should PQclear PGresult whenever it is no longer needed to avoid memory
* leaks
*/
PQclear(res);
/* /*
* Our test case here involves using a cursor, for which we must be inside * Our test case here involves using a cursor, for which we must be inside
* a transaction block. We could do the whole thing with a single * a transaction block. We could do the whole thing with a single
...@@ -8320,11 +8353,6 @@ main(int argc, char **argv) ...@@ -8320,11 +8353,6 @@ main(int argc, char **argv)
PQclear(res); PQclear(res);
exit_nicely(conn); exit_nicely(conn);
} }
/*
* Should PQclear PGresult whenever it is no longer needed to avoid memory
* leaks
*/
PQclear(res); PQclear(res);
/* /*
...@@ -8400,16 +8428,16 @@ main(int argc, char **argv) ...@@ -8400,16 +8428,16 @@ main(int argc, char **argv)
* populate a database with the following commands * populate a database with the following commands
* (provided in src/test/examples/testlibpq2.sql): * (provided in src/test/examples/testlibpq2.sql):
* *
* CREATE SCHEMA TESTLIBPQ2;
* SET search_path = TESTLIBPQ2;
* CREATE TABLE TBL1 (i int4); * CREATE TABLE TBL1 (i int4);
*
* CREATE TABLE TBL2 (i int4); * CREATE TABLE TBL2 (i int4);
*
* CREATE RULE r1 AS ON INSERT TO TBL1 DO * CREATE RULE r1 AS ON INSERT TO TBL1 DO
* (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2); * (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
* *
* and do this four times: * Start this program, then from psql do this four times:
* *
* INSERT INTO TBL1 VALUES (10); * INSERT INTO TESTLIBPQ2.TBL1 VALUES (10);
*/ */
#ifdef WIN32 #ifdef WIN32
...@@ -8464,6 +8492,22 @@ main(int argc, char **argv) ...@@ -8464,6 +8492,22 @@ main(int argc, char **argv)
exit_nicely(conn); exit_nicely(conn);
} }
/* Set always-secure search path, so malicous users can't take control. */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/*
* Should PQclear PGresult whenever it is no longer needed to avoid memory
* leaks
*/
PQclear(res);
/* /*
* Issue LISTEN command to enable notifications from the rule's NOTIFY. * Issue LISTEN command to enable notifications from the rule's NOTIFY.
*/ */
...@@ -8474,11 +8518,6 @@ main(int argc, char **argv) ...@@ -8474,11 +8518,6 @@ main(int argc, char **argv)
PQclear(res); PQclear(res);
exit_nicely(conn); exit_nicely(conn);
} }
/*
* should PQclear PGresult whenever it is no longer needed to avoid memory
* leaks
*/
PQclear(res); PQclear(res);
/* Quit after four notifies are received. */ /* Quit after four notifies are received. */
...@@ -8545,8 +8584,9 @@ main(int argc, char **argv) ...@@ -8545,8 +8584,9 @@ main(int argc, char **argv)
* Before running this, populate a database with the following commands * Before running this, populate a database with the following commands
* (provided in src/test/examples/testlibpq3.sql): * (provided in src/test/examples/testlibpq3.sql):
* *
* CREATE SCHEMA testlibpq3;
* SET search_path = testlibpq3;
* CREATE TABLE test1 (i int4, t text, b bytea); * CREATE TABLE test1 (i int4, t text, b bytea);
*
* INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004'); * INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
* INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000'); * INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
* *
...@@ -8678,6 +8718,16 @@ main(int argc, char **argv) ...@@ -8678,6 +8718,16 @@ main(int argc, char **argv)
exit_nicely(conn); exit_nicely(conn);
} }
/* Set always-secure search path, so malicous users can't take control. */
res = PQexec(conn, "SET search_path = testlibpq3");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
/* /*
* The point of this program is to illustrate use of PQexecParams() with * The point of this program is to illustrate use of PQexecParams() with
* out-of-line parameters, as well as binary transmission of data. * out-of-line parameters, as well as binary transmission of data.
......
...@@ -933,6 +933,17 @@ main(int argc, char **argv) ...@@ -933,6 +933,17 @@ main(int argc, char **argv)
exit_nicely(conn); exit_nicely(conn);
} }
/* Set always-secure search path, so malicous users can't take control. */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
res = PQexec(conn, "begin"); res = PQexec(conn, "begin");
PQclear(res); PQclear(res);
printf("importing file \"%s\" ...\n", in_filename); printf("importing file \"%s\" ...\n", in_filename);
......
...@@ -1688,5 +1688,16 @@ statement latencies in milliseconds: ...@@ -1688,5 +1688,16 @@ statement latencies in milliseconds:
database server. database server.
</para> </para>
</refsect2> </refsect2>
<refsect2>
<title>Security</title>
<para>
If untrusted users have access to a database that has not adopted a
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
do not run <application>pgbench</application> in that
database. <application>pgbench</application> uses unqualified names and
does not manipulate the search path.
</para>
</refsect2>
</refsect1> </refsect1>
</refentry> </refentry>
...@@ -735,6 +735,18 @@ testdb=&gt; ...@@ -735,6 +735,18 @@ testdb=&gt;
of the command are displayed on the screen. of the command are displayed on the screen.
</para> </para>
<para>
If untrusted users have access to a database that has not adopted a
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
begin your session by removing publicly-writable schemas
from <varname>search_path</varname>. One can
add <literal>options=-csearch_path=</literal> to the connection string or
issue <literal>SELECT pg_catalog.set_config('search_path', '',
false)</literal> before other SQL commands. This consideration is not
specific to <application>psql</application>; it applies to every interface
for executing arbitrary SQL commands.
</para>
<para> <para>
Whenever a command is executed, <application>psql</application> also polls Whenever a command is executed, <application>psql</application> also polls
for asynchronous notification events generated by for asynchronous notification events generated by
......
...@@ -571,14 +571,17 @@ GRANT pg_signal_backend TO admin_user; ...@@ -571,14 +571,17 @@ GRANT pg_signal_backend TO admin_user;
</sect1> </sect1>
<sect1 id="perm-functions"> <sect1 id="perm-functions">
<title>Function and Trigger Security</title> <title>Function Security</title>
<para> <para>
Functions and triggers allow users to insert code into the backend Functions, triggers and row-level security policies allow users to insert
server that other users might execute unintentionally. Hence, both code into the backend server that other users might execute
mechanisms permit users to <quote>Trojan horse</quote> unintentionally. Hence, these mechanisms permit users to <quote>Trojan
others with relative ease. The only real protection is tight horse</quote> others with relative ease. The strongest protection is tight
control over who can define functions. control over who can define objects. Where that is infeasible, write
queries referring only to objects having trusted owners. Remove
from <varname>search_path</varname> the public schema and any other schemas
that permit untrusted users to create objects.
</para> </para>
<para> <para>
......
...@@ -48,6 +48,22 @@ main(int argc, char **argv) ...@@ -48,6 +48,22 @@ main(int argc, char **argv)
exit_nicely(conn); exit_nicely(conn);
} }
/* Set always-secure search path, so malicous users can't take control. */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/*
* Should PQclear PGresult whenever it is no longer needed to avoid memory
* leaks
*/
PQclear(res);
/* /*
* Our test case here involves using a cursor, for which we must be inside * Our test case here involves using a cursor, for which we must be inside
* a transaction block. We could do the whole thing with a single * a transaction block. We could do the whole thing with a single
...@@ -63,11 +79,6 @@ main(int argc, char **argv) ...@@ -63,11 +79,6 @@ main(int argc, char **argv)
PQclear(res); PQclear(res);
exit_nicely(conn); exit_nicely(conn);
} }
/*
* Should PQclear PGresult whenever it is no longer needed to avoid memory
* leaks
*/
PQclear(res); PQclear(res);
/* /*
......
...@@ -13,16 +13,16 @@ ...@@ -13,16 +13,16 @@
* populate a database with the following commands * populate a database with the following commands
* (provided in src/test/examples/testlibpq2.sql): * (provided in src/test/examples/testlibpq2.sql):
* *
* CREATE SCHEMA TESTLIBPQ2;
* SET search_path = TESTLIBPQ2;
* CREATE TABLE TBL1 (i int4); * CREATE TABLE TBL1 (i int4);
*
* CREATE TABLE TBL2 (i int4); * CREATE TABLE TBL2 (i int4);
*
* CREATE RULE r1 AS ON INSERT TO TBL1 DO * CREATE RULE r1 AS ON INSERT TO TBL1 DO
* (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2); * (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
* *
* and do this four times: * Start this program, then from psql do this four times:
* *
* INSERT INTO TBL1 VALUES (10); * INSERT INTO TESTLIBPQ2.TBL1 VALUES (10);
*/ */
#ifdef WIN32 #ifdef WIN32
...@@ -77,6 +77,22 @@ main(int argc, char **argv) ...@@ -77,6 +77,22 @@ main(int argc, char **argv)
exit_nicely(conn); exit_nicely(conn);
} }
/* Set always-secure search path, so malicous users can't take control. */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/*
* Should PQclear PGresult whenever it is no longer needed to avoid memory
* leaks
*/
PQclear(res);
/* /*
* Issue LISTEN command to enable notifications from the rule's NOTIFY. * Issue LISTEN command to enable notifications from the rule's NOTIFY.
*/ */
...@@ -87,11 +103,6 @@ main(int argc, char **argv) ...@@ -87,11 +103,6 @@ main(int argc, char **argv)
PQclear(res); PQclear(res);
exit_nicely(conn); exit_nicely(conn);
} }
/*
* should PQclear PGresult whenever it is no longer needed to avoid memory
* leaks
*/
PQclear(res); PQclear(res);
/* Quit after four notifies are received. */ /* Quit after four notifies are received. */
......
CREATE SCHEMA TESTLIBPQ2;
SET search_path = TESTLIBPQ2;
CREATE TABLE TBL1 (i int4); CREATE TABLE TBL1 (i int4);
CREATE TABLE TBL2 (i int4); CREATE TABLE TBL2 (i int4);
CREATE RULE r1 AS ON INSERT TO TBL1 DO CREATE RULE r1 AS ON INSERT TO TBL1 DO
(INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2); (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
...@@ -8,8 +8,9 @@ ...@@ -8,8 +8,9 @@
* Before running this, populate a database with the following commands * Before running this, populate a database with the following commands
* (provided in src/test/examples/testlibpq3.sql): * (provided in src/test/examples/testlibpq3.sql):
* *
* CREATE SCHEMA testlibpq3;
* SET search_path = testlibpq3;
* CREATE TABLE test1 (i int4, t text, b bytea); * CREATE TABLE test1 (i int4, t text, b bytea);
*
* INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004'); * INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
* INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000'); * INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
* *
...@@ -141,6 +142,16 @@ main(int argc, char **argv) ...@@ -141,6 +142,16 @@ main(int argc, char **argv)
exit_nicely(conn); exit_nicely(conn);
} }
/* Set always-secure search path, so malicous users can't take control. */
res = PQexec(conn, "SET search_path = testlibpq3");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
/* /*
* The point of this program is to illustrate use of PQexecParams() with * The point of this program is to illustrate use of PQexecParams() with
* out-of-line parameters, as well as binary transmission of data. * out-of-line parameters, as well as binary transmission of data.
......
CREATE SCHEMA testlibpq3;
SET search_path = testlibpq3;
CREATE TABLE test1 (i int4, t text, b bytea); CREATE TABLE test1 (i int4, t text, b bytea);
INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004'); INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000'); INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
...@@ -22,8 +22,10 @@ exit_nicely(PGconn *conn1, PGconn *conn2) ...@@ -22,8 +22,10 @@ exit_nicely(PGconn *conn1, PGconn *conn2)
} }
static void static void
check_conn(PGconn *conn, const char *dbName) check_prepare_conn(PGconn *conn, const char *dbName)
{ {
PGresult *res;
/* check to see that the backend connection was successfully made */ /* check to see that the backend connection was successfully made */
if (PQstatus(conn) != CONNECTION_OK) if (PQstatus(conn) != CONNECTION_OK)
{ {
...@@ -31,6 +33,17 @@ check_conn(PGconn *conn, const char *dbName) ...@@ -31,6 +33,17 @@ check_conn(PGconn *conn, const char *dbName)
dbName, PQerrorMessage(conn)); dbName, PQerrorMessage(conn));
exit(1); exit(1);
} }
/* Set always-secure search path, so malicous users can't take control. */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit(1);
}
PQclear(res);
} }
int int
...@@ -80,10 +93,10 @@ main(int argc, char **argv) ...@@ -80,10 +93,10 @@ main(int argc, char **argv)
/* make a connection to the database */ /* make a connection to the database */
conn1 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName1); conn1 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName1);
check_conn(conn1, dbName1); check_prepare_conn(conn1, dbName1);
conn2 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName2); conn2 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName2);
check_conn(conn2, dbName2); check_prepare_conn(conn2, dbName2);
/* start a transaction block */ /* start a transaction block */
res1 = PQexec(conn1, "BEGIN"); res1 = PQexec(conn1, "BEGIN");
......
...@@ -232,6 +232,17 @@ main(int argc, char **argv) ...@@ -232,6 +232,17 @@ main(int argc, char **argv)
exit_nicely(conn); exit_nicely(conn);
} }
/* Set always-secure search path, so malicous users can't take control. */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
res = PQexec(conn, "begin"); res = PQexec(conn, "begin");
PQclear(res); PQclear(res);
printf("importing file \"%s\" ...\n", in_filename); printf("importing file \"%s\" ...\n", in_filename);
......
...@@ -256,6 +256,17 @@ main(int argc, char **argv) ...@@ -256,6 +256,17 @@ main(int argc, char **argv)
exit_nicely(conn); exit_nicely(conn);
} }
/* Set always-secure search path, so malicous users can't take control. */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
res = PQexec(conn, "begin"); res = PQexec(conn, "begin");
PQclear(res); PQclear(res);
printf("importing file \"%s\" ...\n", in_filename); printf("importing file \"%s\" ...\n", in_filename);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment