From 919c9f6cceeae8468672462a23eab470e18ceda0 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 3 Jan 2008 21:27:59 +0000
Subject: [PATCH] The original patch to disallow non-passworded connections to
 non-superusers failed to cover all the ways in which a connection can be
 initiated in dblink. Plug the remaining holes.  Also, disallow transient
 connections in functions for which that feature makes no sense (because they
 are only sensible as part of a sequence of operations on the same
 connection).  Joe Conway

Security: CVE-2007-6601
---
 contrib/dblink/dblink.c            | 78 +++++++++++++++---------------
 contrib/dblink/expected/dblink.out | 37 ++++++++++++++
 contrib/dblink/sql/dblink.sql      |  9 ++++
 3 files changed, 86 insertions(+), 38 deletions(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 0376345d81a..92891a03557 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -8,7 +8,7 @@
  * Darko Prenosil <Darko.Prenosil@finteh.hr>
  * Shridhar Daithankar <shridhar_daithankar@persistent.co.in>
  *
- * $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.67 2008/01/01 19:45:45 momjian Exp $
+ * $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.68 2008/01/03 21:27:59 tgl Exp $
  * Copyright (c) 2001-2008, PostgreSQL Global Development Group
  * ALL RIGHTS RESERVED;
  *
@@ -91,6 +91,7 @@ static int16 get_attnum_pk_pos(int2vector *pkattnums, int16 pknumatts, int16 key
 static HeapTuple get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals);
 static Oid	get_relid_from_relname(text *relname_text);
 static char *generate_relation_name(Oid relid);
+static void dblink_security_check(PGconn *conn, remoteConn *rconn);
 
 /* Global */
 static remoteConn *pconn = NULL;
@@ -187,10 +188,21 @@ typedef struct remoteConnHashEnt
 							 errmsg("could not establish connection"), \
 							 errdetail("%s", msg))); \
 				} \
+				dblink_security_check(conn, rconn); \
 				freeconn = true; \
 			} \
 	} while (0)
 
+#define DBLINK_GET_NAMED_CONN \
+	do { \
+			char *conname = GET_STR(PG_GETARG_TEXT_P(0)); \
+			rconn = getConnectionByName(conname); \
+			if(rconn) \
+				conn = rconn->conn; \
+			else \
+				DBLINK_CONN_NOT_AVAIL; \
+	} while (0)
+
 #define DBLINK_INIT \
 	do { \
 			if (!pconn) \
@@ -247,21 +259,8 @@ dblink_connect(PG_FUNCTION_ARGS)
 				 errdetail("%s", msg)));
 	}
 
-	if (!superuser())
-	{
-		if (!PQconnectionUsedPassword(conn))
-		{
-			PQfinish(conn);
-			if (rconn)
-				pfree(rconn);
-
-			ereport(ERROR,
-				  (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-				   errmsg("password is required"),
-				   errdetail("Non-superuser cannot connect if the server does not request a password."),
-				   errhint("Target server's authentication method must be changed.")));
-		}
-	}
+	/* check password used if not superuser */
+	dblink_security_check(conn, rconn);
 
 	if (connname)
 	{
@@ -1047,17 +1046,11 @@ PG_FUNCTION_INFO_V1(dblink_is_busy);
 Datum
 dblink_is_busy(PG_FUNCTION_ARGS)
 {
-	char	   *msg;
 	PGconn	   *conn = NULL;
-	char	   *conname = NULL;
-	char	   *connstr = NULL;
 	remoteConn *rconn = NULL;
-	bool		freeconn = false;
 
 	DBLINK_INIT;
-	DBLINK_GET_CONN;
-	if (!conn)
-		DBLINK_CONN_NOT_AVAIL;
+	DBLINK_GET_NAMED_CONN;
 
 	PQconsumeInput(conn);
 	PG_RETURN_INT32(PQisBusy(conn));
@@ -1078,26 +1071,20 @@ PG_FUNCTION_INFO_V1(dblink_cancel_query);
 Datum
 dblink_cancel_query(PG_FUNCTION_ARGS)
 {
-	char	   *msg;
 	int			res = 0;
 	PGconn	   *conn = NULL;
-	char	   *conname = NULL;
-	char	   *connstr = NULL;
 	remoteConn *rconn = NULL;
-	bool		freeconn = false;
 	PGcancel   *cancel;
 	char		errbuf[256];
 
 	DBLINK_INIT;
-	DBLINK_GET_CONN;
-	if (!conn)
-		DBLINK_CONN_NOT_AVAIL;
+	DBLINK_GET_NAMED_CONN;
 	cancel = PQgetCancel(conn);
 
 	res = PQcancel(cancel, errbuf, 256);
 	PQfreeCancel(cancel);
 
-	if (res == 0)
+	if (res == 1)
 		PG_RETURN_TEXT_P(GET_TEXT("OK"));
 	else
 		PG_RETURN_TEXT_P(GET_TEXT(errbuf));
@@ -1120,18 +1107,13 @@ dblink_error_message(PG_FUNCTION_ARGS)
 {
 	char	   *msg;
 	PGconn	   *conn = NULL;
-	char	   *conname = NULL;
-	char	   *connstr = NULL;
 	remoteConn *rconn = NULL;
-	bool		freeconn = false;
 
 	DBLINK_INIT;
-	DBLINK_GET_CONN;
-	if (!conn)
-		DBLINK_CONN_NOT_AVAIL;
+	DBLINK_GET_NAMED_CONN;
 
 	msg = PQerrorMessage(conn);
-	if (!msg)
+	if (msg == NULL || msg[0] == '\0')
 		PG_RETURN_TEXT_P(GET_TEXT("OK"));
 	else
 		PG_RETURN_TEXT_P(GET_TEXT(msg));
@@ -2299,3 +2281,23 @@ deleteConnection(const char *name)
 				 errmsg("undefined connection name")));
 
 }
+
+static void
+dblink_security_check(PGconn *conn, remoteConn *rconn)
+{
+	if (!superuser())
+	{
+		if (!PQconnectionUsedPassword(conn))
+		{
+			PQfinish(conn);
+			if (rconn)
+				pfree(rconn);
+
+			ereport(ERROR,
+				  (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+				   errmsg("password is required"),
+				   errdetail("Non-superuser cannot connect if the server does not request a password."),
+				   errhint("Target server's authentication method must be changed.")));
+		}
+	}
+}
diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out
index bdd2f9926f6..fd35d76af96 100644
--- a/contrib/dblink/expected/dblink.out
+++ b/contrib/dblink/expected/dblink.out
@@ -724,6 +724,12 @@ SELECT dblink_get_connections();
  {dtest1,dtest2,dtest3}
 (1 row)
 
+SELECT dblink_is_busy('dtest1');
+ dblink_is_busy 
+----------------
+              0
+(1 row)
+
 SELECT dblink_disconnect('dtest1');
  dblink_disconnect 
 -------------------
@@ -758,3 +764,34 @@ SELECT * from result;
  10 | k  | {a10,b10,c10}
 (11 rows)
 
+SELECT dblink_connect('dtest1', 'dbname=contrib_regression');
+ dblink_connect 
+----------------
+ OK
+(1 row)
+
+SELECT * from 
+ dblink_send_query('dtest1', 'select * from foo where f1 < 3') as t1;
+ t1 
+----
+  1
+(1 row)
+
+SELECT dblink_cancel_query('dtest1');
+ dblink_cancel_query 
+---------------------
+ OK
+(1 row)
+
+SELECT dblink_error_message('dtest1');
+ dblink_error_message 
+----------------------
+ OK
+(1 row)
+
+SELECT dblink_disconnect('dtest1');
+ dblink_disconnect 
+-------------------
+ OK
+(1 row)
+
diff --git a/contrib/dblink/sql/dblink.sql b/contrib/dblink/sql/dblink.sql
index 277500e6f16..48e1daca54d 100644
--- a/contrib/dblink/sql/dblink.sql
+++ b/contrib/dblink/sql/dblink.sql
@@ -344,9 +344,18 @@ UNION
 ORDER by f1;
 
 SELECT dblink_get_connections();
+SELECT dblink_is_busy('dtest1');
 
 SELECT dblink_disconnect('dtest1');
 SELECT dblink_disconnect('dtest2');
 SELECT dblink_disconnect('dtest3');
+
 SELECT * from result;
 
+SELECT dblink_connect('dtest1', 'dbname=contrib_regression');
+SELECT * from 
+ dblink_send_query('dtest1', 'select * from foo where f1 < 3') as t1;
+
+SELECT dblink_cancel_query('dtest1');
+SELECT dblink_error_message('dtest1');
+SELECT dblink_disconnect('dtest1');
-- 
GitLab