#include "libpq-fe.h"
 #include "fmgr.h"
 #include "funcapi.h"
+#include "miscadmin.h"
 #include "access/tupdesc.h"
 #include "access/heapam.h"
 #include "catalog/catname.h"
 static HeapTuple get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals);
 static Oid     get_relid_from_relname(text *relname_text);
 static char *generate_relation_name(Oid relid);
+static char *connstr_strip_password(const char *connstr);
 
 /* Global */
 List      *res_id = NIL;
 
        if (connname)
                rcon = (remoteConn *) palloc(sizeof(remoteConn));
+
+       /* for non-superusers, check that server requires a password */
+       if (!superuser())
+       {
+               /* this attempt must fail */
+               conn = PQconnectdb(connstr_strip_password(connstr));
+
+               if (PQstatus(conn) == CONNECTION_OK)
+               {
+                       PQfinish(conn);
+                       if (rcon)
+                               pfree(rcon);
+
+                       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.")));
+               }
+               else
+                       PQfinish(conn);
+       }
        conn = PQconnectdb(connstr);
 
        MemoryContextSwitchTo(oldcontext);
                                 errmsg("undefined connection name")));
 
 }
+
+/*
+ * 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 != '=')
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                       errmsg("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')
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                       errmsg("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;
+}
 
+-- dblink_connect now restricts non-superusers to password
+-- authenticated connections
 CREATE OR REPLACE FUNCTION dblink_connect (text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_connect'
 AS 'MODULE_PATHNAME','dblink_connect'
 LANGUAGE 'C' STRICT;
 
+-- 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;
+
+CREATE OR REPLACE FUNCTION dblink_connect_u (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE C STRICT SECURITY DEFINER;
+
+REVOKE ALL ON FUNCTION dblink_connect_u (text) FROM public;
+REVOKE ALL ON FUNCTION dblink_connect_u (text, text) FROM public;
+
 CREATE OR REPLACE FUNCTION dblink_disconnect ()
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_disconnect'
 
 
   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
 
 select dblink_connect('dbname=template1');
  OK
 (1 row)
 
+==================================================================
+Name
+
+dblink_connect_u -- Opens a persistent connection to a remote database
+
+Synopsis
+
+dblink_connect_u(text connstr)
+dblink_connect_u(text connname, text connstr)
+
+Inputs
+
+  connname
+    if 2 arguments are given, the first is used as a name for a persistent
+    connection
+
+  connstr
+
+    standard libpq format connection string, 
+    e.g. "hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd"
+
+    if only one argument is given, the connection is unnamed; only one unnamed
+    connection can exist at a time
+
+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