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.
This commit is contained in:
Joe Conway 2007-07-09 01:32:30 +00:00
parent 18e47a572c
commit 809b38ce27
3 changed files with 221 additions and 2 deletions

View File

@ -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.60 2006/10/19 19:53:03 tgl Exp $
* $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.60.2.1 2007/07/09 01:32:30 joe Exp $
* Copyright (c) 2001-2006, PostgreSQL Global Development Group
* ALL RIGHTS RESERVED;
*
@ -37,6 +37,7 @@
#include "libpq-fe.h"
#include "fmgr.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "access/heapam.h"
#include "access/tupdesc.h"
#include "catalog/namespace.h"
@ -89,6 +90,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 char *connstr_strip_password(const char *connstr);
/* Global */
static remoteConn *pconn = NULL;
@ -228,6 +230,28 @@ dblink_connect(PG_FUNCTION_ARGS)
if (connname)
rconn = (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 (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.")));
}
else
PQfinish(conn);
}
conn = PQconnectdb(connstr);
MemoryContextSwitchTo(oldcontext);
@ -2273,3 +2297,134 @@ deleteConnection(const char *name)
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;
}

View File

@ -1,3 +1,5 @@
-- dblink_connect now restricts non-superusers to password
-- authenticated connections
CREATE OR REPLACE FUNCTION dblink_connect (text)
RETURNS text
AS 'MODULE_PATHNAME','dblink_connect'
@ -8,6 +10,22 @@ RETURNS text
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'

View File

@ -1,4 +1,4 @@
$PostgreSQL: pgsql/contrib/dblink/doc/connection,v 1.4 2006/03/11 04:38:29 momjian Exp $
$PostgreSQL: pgsql/contrib/dblink/doc/connection,v 1.4.2.1 2007/07/09 01:32:30 joe Exp $
==================================================================
Name
@ -27,6 +27,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
select dblink_connect('dbname=postgres');
@ -41,6 +47,46 @@ select dblink_connect('myconn','dbname=postgres');
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