diff options
-rw-r--r-- | contrib/dblink/dblink.c | 144 | ||||
-rw-r--r-- | contrib/dblink/dblink.sql.in | 12 | ||||
-rw-r--r-- | contrib/dblink/doc/connection | 38 |
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 |