Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+47-2
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

+10-3
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

+104-35
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

+2-1
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)