diff options
author | Joe Conway | 2007-07-09 01:44:11 +0000 |
---|---|---|
committer | Joe Conway | 2007-07-09 01:44:11 +0000 |
commit | a776eaea3c661314e8fb14d69d627eebf11f3fef (patch) | |
tree | a4a89fe46d7b4d066d636318263d934bd30bdb39 | |
parent | 7ca642207596fbc4c42701e97a376e5a6a377976 (diff) |
Restrict non-superusers to password authenticated connections
to prevent possible escalation of privilege. Provide new SECURITY
DEFINER functions with old behavior, but initially REVOKE ALL
from public for these functions. Per list discussion and design
proposed by Tom Lane.
-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 |