summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/dblink/dblink.c144
-rw-r--r--contrib/dblink/dblink.sql.in12
-rw-r--r--contrib/dblink/doc/connection38
3 files changed, 194 insertions, 0 deletions
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 4d52f8d1069..a7809317dc0 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -32,6 +32,7 @@
#include "fmgr.h"
#include "funcapi.h"
+#include "miscadmin.h"
#include "access/tupdesc.h"
#include "access/heapam.h"
#include "catalog/catname.h"
@@ -71,6 +72,7 @@ static dblink_results *get_res_ptr(int32 res_id_index);
static void append_res_ptr(dblink_results * results);
static void remove_res_ptr(dblink_results * results);
static char *generate_relation_name(Oid relid);
+static char *connstr_strip_password(const char *connstr);
/* Global */
List *res_id = NIL;
@@ -105,6 +107,22 @@ dblink_connect(PG_FUNCTION_ARGS)
PQfinish(persistent_conn);
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+ /* for non-superusers, check that server requires a password */
+ if (!superuser())
+ {
+ /* this attempt must fail */
+ persistent_conn = PQconnectdb(connstr_strip_password(connstr));
+
+ if (PQstatus(persistent_conn) == CONNECTION_OK)
+ {
+ PQfinish(persistent_conn);
+ persistent_conn = NULL;
+ elog(ERROR, "Non-superuser cannot connect if the server does not request a password.");
+ }
+ else
+ PQfinish(persistent_conn);
+ }
persistent_conn = PQconnectdb(connstr);
MemoryContextSwitchTo(oldcontext);
@@ -2032,3 +2050,129 @@ generate_relation_name(Oid relid)
return result;
}
+
+/*
+ * Modified version of conninfo_parse() from fe-connect.c
+ * Used to remove any password from the connection string
+ * in order to test whether the server auth method will
+ * require it.
+ */
+static char *
+connstr_strip_password(const char *connstr)
+{
+ char *pname;
+ char *pval;
+ char *buf;
+ char *cp;
+ char *cp2;
+ StringInfoData result;
+
+ /* initialize return value */
+ initStringInfo(&result);
+
+ /* Need a modifiable copy of the input string */
+ buf = pstrdup(connstr);
+ cp = buf;
+
+ while (*cp)
+ {
+ /* Skip blanks before the parameter name */
+ if (isspace((unsigned char) *cp))
+ {
+ cp++;
+ continue;
+ }
+
+ /* Get the parameter name */
+ pname = cp;
+ while (*cp)
+ {
+ if (*cp == '=')
+ break;
+ if (isspace((unsigned char) *cp))
+ {
+ *cp++ = '\0';
+ while (*cp)
+ {
+ if (!isspace((unsigned char) *cp))
+ break;
+ cp++;
+ }
+ break;
+ }
+ cp++;
+ }
+
+ /* Check that there is a following '=' */
+ if (*cp != '=')
+ elog(ERROR, "missing \"=\" after \"%s\" in connection string", pname);
+ *cp++ = '\0';
+
+ /* Skip blanks after the '=' */
+ while (*cp)
+ {
+ if (!isspace((unsigned char) *cp))
+ break;
+ cp++;
+ }
+
+ /* Get the parameter value */
+ pval = cp;
+
+ if (*cp != '\'')
+ {
+ cp2 = pval;
+ while (*cp)
+ {
+ if (isspace((unsigned char) *cp))
+ {
+ *cp++ = '\0';
+ break;
+ }
+ if (*cp == '\\')
+ {
+ cp++;
+ if (*cp != '\0')
+ *cp2++ = *cp++;
+ }
+ else
+ *cp2++ = *cp++;
+ }
+ *cp2 = '\0';
+ }
+ else
+ {
+ cp2 = pval;
+ cp++;
+ for (;;)
+ {
+ if (*cp == '\0')
+ elog(ERROR, "unterminated quoted string in connection string");
+ if (*cp == '\\')
+ {
+ cp++;
+ if (*cp != '\0')
+ *cp2++ = *cp++;
+ continue;
+ }
+ if (*cp == '\'')
+ {
+ *cp2 = '\0';
+ cp++;
+ break;
+ }
+ *cp2++ = *cp++;
+ }
+ }
+
+ /*
+ * Now we have the name and the value. If it is not a password,
+ * append to the return connstr.
+ */
+ if (strcmp("password", pname) != 0)
+ /* append the value */
+ appendStringInfo(&result, " %s='%s'", pname, pval);
+ }
+
+ return result.data;
+}
diff --git a/contrib/dblink/dblink.sql.in b/contrib/dblink/dblink.sql.in
index 42e483de34e..081caad915b 100644
--- a/contrib/dblink/dblink.sql.in
+++ b/contrib/dblink/dblink.sql.in
@@ -14,11 +14,23 @@
--AS 'MODULE_PATHNAME','dblink_last_oid'
--LANGUAGE 'C' WITH (isstrict);
+-- dblink_connect now restricts non-superusers to password
+-- authenticated connections
CREATE OR REPLACE FUNCTION dblink_connect (text)
RETURNS text
AS 'MODULE_PATHNAME','dblink_connect'
LANGUAGE 'C' WITH (isstrict);
+-- dblink_connect_u allows non-superusers to use
+-- non-password authenticated connections, but initially
+-- privileges are revoked from public
+CREATE OR REPLACE FUNCTION dblink_connect_u (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE C STRICT SECURITY DEFINER;
+
+REVOKE ALL ON FUNCTION dblink_connect_u (text) FROM public;
+
CREATE OR REPLACE FUNCTION dblink_disconnect ()
RETURNS text
AS 'MODULE_PATHNAME','dblink_disconnect'
diff --git a/contrib/dblink/doc/connection b/contrib/dblink/doc/connection
index 3a749d8903b..cbe78c0d20d 100644
--- a/contrib/dblink/doc/connection
+++ b/contrib/dblink/doc/connection
@@ -18,6 +18,12 @@ Outputs
Returns status = "OK"
+Notes
+
+ Only superusers may use dblink_connect to create non-password
+ authenticated connections. If non-superusers need this capability,
+ use dblink_connect_u instead.
+
Example usage
test=# select dblink_connect('dbname=template1');
@@ -29,6 +35,38 @@ test=# select dblink_connect('dbname=template1');
==================================================================
Name
+dblink_connect_u -- Opens a persistent connection to a remote database
+
+Synopsis
+
+dblink_connect_u(text connstr)
+
+Inputs
+
+ connstr
+
+ standard libpq format connection string,
+ e.g. "hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd"
+
+Outputs
+
+ Returns status = "OK"
+
+Notes
+
+ With dblink_connect_u, a non-superuser may connect to any database server
+ using any authentication method. If the authentication method specified
+ for a particular user does not require a password, impersonation and
+ therefore escalation of privileges may occur. For this reason,
+ dblink_connect_u is initially installed with all privileges revoked from
+ public. Privilege to these functions should be granted with care.
+
+Example usage
+
+
+==================================================================
+Name
+
dblink_disconnect -- Closes the persistent connection to a remote database
Synopsis