Skip to content

Commit cae7ad9

Browse files
committed
Fix dblink_connect() so that it verifies that a password is supplied in the
conninfo string *before* trying to connect to the remote server, not after. As pointed out by Marko Kreen, in certain not-very-plausible situations this could result in sending a password from the postgres user's .pgpass file, or other places that non-superusers shouldn't have access to, to an untrustworthy remote server. The cleanest fix seems to be to expose libpq's conninfo-string-parsing code so that dblink can check for a password option without duplicating the parsing logic. Joe Conway, with a little cleanup by Tom Lane
1 parent 579c025 commit cae7ad9

File tree

6 files changed

+226
-50
lines changed

6 files changed

+226
-50
lines changed

contrib/dblink/dblink.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Darko Prenosil <Darko.Prenosil@finteh.hr>
99
* Shridhar Daithankar <shridhar_daithankar@persistent.co.in>
1010
*
11-
* $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.74 2008/07/03 03:56:57 joe Exp $
11+
* $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.75 2008/09/22 13:55:13 tgl Exp $
1212
* Copyright (c) 2001-2008, PostgreSQL Global Development Group
1313
* ALL RIGHTS RESERVED;
1414
*
@@ -93,6 +93,7 @@ static int16 get_attnum_pk_pos(int2vector *pkattnums, int16 pknumatts, int16 key
9393
static HeapTuple get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals);
9494
static Oid get_relid_from_relname(text *relname_text);
9595
static char *generate_relation_name(Oid relid);
96+
static void dblink_connstr_check(const char *connstr);
9697
static void dblink_security_check(PGconn *conn, remoteConn *rconn);
9798
static void dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail);
9899

@@ -165,6 +166,7 @@ typedef struct remoteConnHashEnt
165166
else \
166167
{ \
167168
connstr = conname_or_str; \
169+
dblink_connstr_check(connstr); \
168170
conn = PQconnectdb(connstr); \
169171
if (PQstatus(conn) == CONNECTION_BAD) \
170172
{ \
@@ -229,6 +231,9 @@ dblink_connect(PG_FUNCTION_ARGS)
229231

230232
if (connname)
231233
rconn = (remoteConn *) palloc(sizeof(remoteConn));
234+
235+
/* check password in connection string if not superuser */
236+
dblink_connstr_check(connstr);
232237
conn = PQconnectdb(connstr);
233238

234239
MemoryContextSwitchTo(oldcontext);
@@ -246,7 +251,7 @@ dblink_connect(PG_FUNCTION_ARGS)
246251
errdetail("%s", msg)));
247252
}
248253

249-
/* check password used if not superuser */
254+
/* check password actually used if not superuser */
250255
dblink_security_check(conn, rconn);
251256

252257
if (connname)
@@ -2251,6 +2256,46 @@ dblink_security_check(PGconn *conn, remoteConn *rconn)
22512256
}
22522257
}
22532258

2259+
/*
2260+
* For non-superusers, insist that the connstr specify a password. This
2261+
* prevents a password from being picked up from .pgpass, a service file,
2262+
* the environment, etc. We don't want the postgres user's passwords
2263+
* to be accessible to non-superusers.
2264+
*/
2265+
static void
2266+
dblink_connstr_check(const char *connstr)
2267+
{
2268+
if (!superuser())
2269+
{
2270+
PQconninfoOption *options;
2271+
PQconninfoOption *option;
2272+
bool connstr_gives_password = false;
2273+
2274+
options = PQconninfoParse(connstr, NULL);
2275+
if (options)
2276+
{
2277+
for (option = options; option->keyword != NULL; option++)
2278+
{
2279+
if (strcmp(option->keyword, "password") == 0)
2280+
{
2281+
if (option->val != NULL && option->val[0] != '\0')
2282+
{
2283+
connstr_gives_password = true;
2284+
break;
2285+
}
2286+
}
2287+
}
2288+
PQconninfoFree(options);
2289+
}
2290+
2291+
if (!connstr_gives_password)
2292+
ereport(ERROR,
2293+
(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
2294+
errmsg("password is required"),
2295+
errdetail("Non-superusers must provide a password in the connection string.")));
2296+
}
2297+
}
2298+
22542299
static void
22552300
dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail)
22562301
{

doc/src/sgml/dblink.sgml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/dblink.sgml,v 1.4 2008/04/04 16:57:21 momjian Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/dblink.sgml,v 1.5 2008/09/22 13:55:13 tgl Exp $ -->
22

33
<sect1 id="dblink">
44
<title>dblink</title>
@@ -140,12 +140,19 @@
140140
involve a password, then impersonation and subsequent escalation of
141141
privileges can occur, because the session will appear to have
142142
originated from the user as which the local <productname>PostgreSQL</>
143-
server runs. Therefore, <function>dblink_connect_u()</> is initially
143+
server runs. Also, even if the remote server does demand a password,
144+
it is possible for the password to be supplied from the server
145+
environment, such as a <filename>~/.pgpass</> file belonging to the
146+
server's user. This opens not only a risk of impersonation, but the
147+
possibility of exposing a password to an untrustworthy remote server.
148+
Therefore, <function>dblink_connect_u()</> is initially
144149
installed with all privileges revoked from <literal>PUBLIC</>,
145150
making it un-callable except by superusers. In some situations
146151
it may be appropriate to grant <literal>EXECUTE</> permission for
147152
<function>dblink_connect_u()</> to specific users who are considered
148-
trustworthy, but this should be done with care.
153+
trustworthy, but this should be done with care. It is also recommended
154+
that any <filename>~/.pgpass</> file belonging to the server's user
155+
<emphasis>not</> contain any records specifying a wildcard host name.
149156
</para>
150157

151158
<para>

doc/src/sgml/libpq.sgml

Lines changed: 104 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.263 2008/09/19 20:06:13 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.264 2008/09/22 13:55:13 tgl Exp $ -->
22

33
<chapter id="libpq">
44
<title><application>libpq</application> - C Library</title>
@@ -593,7 +593,7 @@ typedef struct
593593
char *compiled; /* Fallback compiled in default value */
594594
char *val; /* Option's current value, or NULL */
595595
char *label; /* Label for field in connect dialog */
596-
char *dispchar; /* Character to display for this field
596+
char *dispchar; /* Indicates how to display this field
597597
in a connect dialog. Values are:
598598
"" Display entered value as is
599599
"*" Password field - hide value
@@ -624,6 +624,51 @@ typedef struct
624624
</listitem>
625625
</varlistentry>
626626

627+
<varlistentry>
628+
<term><function>PQconninfoParse</function><indexterm><primary>PQconninfoParse</></></term>
629+
<listitem>
630+
<para>
631+
Returns parsed connection options from the provided connection string.
632+
633+
<synopsis>
634+
PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg);
635+
</synopsis>
636+
</para>
637+
638+
<para>
639+
Parses a connection string and returns the resulting options as an
640+
array; or returns NULL if there is a problem with the connection
641+
string. This can be used to determine
642+
the <function>PQconnectdb</function> options in the provided
643+
connection string. The return value points to an array of
644+
<structname>PQconninfoOption</structname> structures, which ends
645+
with an entry having a null <structfield>keyword</> pointer.
646+
</para>
647+
648+
<para>
649+
Note that only options explicitly specified in the string will have
650+
values set in the result array; no defaults are inserted.
651+
</para>
652+
653+
<para>
654+
If <literal>errmsg</> is not NULL, then <literal>*errmsg</> is set
655+
to NULL on success, else to a malloc'd error string explaining
656+
the problem. (It is also possible for <literal>*errmsg</> to be
657+
set to NULL even when NULL is returned; this indicates an out-of-memory
658+
situation.)
659+
</para>
660+
661+
<para>
662+
After processing the options array, free it by passing it to
663+
<function>PQconninfoFree</function>. If this is not done, some memory
664+
is leaked for each call to <function>PQconninfoParse</function>.
665+
Conversely, if an error occurs and <literal>errmsg</> is not NULL,
666+
be sure to free the error string using <function>PQfreemem</>.
667+
</para>
668+
669+
</listitem>
670+
</varlistentry>
671+
627672
<varlistentry>
628673
<term><function>PQfinish</function><indexterm><primary>PQfinish</></></term>
629674
<listitem>
@@ -2985,39 +3030,6 @@ typedef struct {
29853030
</para>
29863031
</listitem>
29873032
</varlistentry>
2988-
2989-
<varlistentry>
2990-
<term>
2991-
<function>PQfreemem</function>
2992-
<indexterm>
2993-
<primary>PQfreemem</primary>
2994-
</indexterm>
2995-
</term>
2996-
2997-
<listitem>
2998-
<para>
2999-
Frees memory allocated by <application>libpq</>.
3000-
<synopsis>
3001-
void PQfreemem(void *ptr);
3002-
</synopsis>
3003-
</para>
3004-
3005-
<para>
3006-
Frees memory allocated by <application>libpq</>, particularly
3007-
<function>PQescapeByteaConn</function>,
3008-
<function>PQescapeBytea</function>,
3009-
<function>PQunescapeBytea</function>,
3010-
and <function>PQnotifies</function>.
3011-
It is particularly important that this function, rather than
3012-
<function>free()</>, be used on Microsoft Windows. This is because
3013-
allocating memory in a DLL and releasing it in the application works
3014-
only if multithreaded/single-threaded, release/debug, and static/dynamic
3015-
flags are the same for the DLL and the application. On non-Microsoft
3016-
Windows platforms, this function is the same as the standard library
3017-
function <function>free()</>.
3018-
</para>
3019-
</listitem>
3020-
</varlistentry>
30213033
</variablelist>
30223034

30233035
</sect2>
@@ -4537,6 +4549,63 @@ char *pg_encoding_to_char(int <replaceable>encoding_id</replaceable>);
45374549
</para>
45384550

45394551
<variablelist>
4552+
<varlistentry>
4553+
<term>
4554+
<function>PQfreemem</function>
4555+
<indexterm>
4556+
<primary>PQfreemem</primary>
4557+
</indexterm>
4558+
</term>
4559+
4560+
<listitem>
4561+
<para>
4562+
Frees memory allocated by <application>libpq</>.
4563+
<synopsis>
4564+
void PQfreemem(void *ptr);
4565+
</synopsis>
4566+
</para>
4567+
4568+
<para>
4569+
Frees memory allocated by <application>libpq</>, particularly
4570+
<function>PQescapeByteaConn</function>,
4571+
<function>PQescapeBytea</function>,
4572+
<function>PQunescapeBytea</function>,
4573+
and <function>PQnotifies</function>.
4574+
It is particularly important that this function, rather than
4575+
<function>free()</>, be used on Microsoft Windows. This is because
4576+
allocating memory in a DLL and releasing it in the application works
4577+
only if multithreaded/single-threaded, release/debug, and static/dynamic
4578+
flags are the same for the DLL and the application. On non-Microsoft
4579+
Windows platforms, this function is the same as the standard library
4580+
function <function>free()</>.
4581+
</para>
4582+
</listitem>
4583+
</varlistentry>
4584+
4585+
<varlistentry>
4586+
<term>
4587+
<function>PQconninfoFree</function>
4588+
<indexterm>
4589+
<primary>PQconninfoFree</primary>
4590+
</indexterm>
4591+
</term>
4592+
4593+
<listitem>
4594+
<para>
4595+
Frees the data structures allocated by
4596+
<function>PQconndefaults</> or <function>PQconninfoParse</>.
4597+
<synopsis>
4598+
void PQconninfoFree(PQconninfoOption *connOptions);
4599+
</synopsis>
4600+
</para>
4601+
4602+
<para>
4603+
A simple <function>PQfreemem</function> will not do for this, since
4604+
the array contains references to subsidiary strings.
4605+
</para>
4606+
</listitem>
4607+
</varlistentry>
4608+
45404609
<varlistentry>
45414610
<term>
45424611
<function>PQencryptPassword</function>

src/interfaces/libpq/exports.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.21 2008/09/19 20:06:13 tgl Exp $
1+
# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.22 2008/09/22 13:55:14 tgl Exp $
22
# Functions to be exported by libpq DLLs
33
PQconnectdb 1
44
PQsetdbLogin 2
@@ -151,3 +151,4 @@ PQsetInstanceData 148
151151
PQresultInstanceData 149
152152
PQresultSetInstanceData 150
153153
PQfireResultCreateEvents 151
154+
PQconninfoParse 152

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy