aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/dblink/dblink.c127
-rw-r--r--contrib/dblink/expected/dblink.out4
-rw-r--r--contrib/postgres_fdw/connection.c72
-rw-r--r--contrib/postgres_fdw/expected/postgres_fdw.out19
-rw-r--r--contrib/postgres_fdw/option.c6
-rw-r--r--contrib/postgres_fdw/sql/postgres_fdw.sql3
6 files changed, 158 insertions, 73 deletions
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 78a8bcee6e3..55f75eff361 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -48,6 +48,7 @@
#include "funcapi.h"
#include "lib/stringinfo.h"
#include "libpq-fe.h"
+#include "libpq/libpq-be.h"
#include "libpq/libpq-be-fe-helpers.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -111,7 +112,8 @@ static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumat
static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode);
static char *generate_relation_name(Relation rel);
static void dblink_connstr_check(const char *connstr);
-static void dblink_security_check(PGconn *conn, remoteConn *rconn);
+static bool dblink_connstr_has_pw(const char *connstr);
+static void dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr);
static void dblink_res_error(PGconn *conn, const char *conname, PGresult *res,
bool fail, const char *fmt,...) pg_attribute_printf(5, 6);
static char *get_connect_string(const char *servername);
@@ -213,7 +215,7 @@ dblink_get_conn(char *conname_or_str,
errmsg("could not establish connection"),
errdetail_internal("%s", msg)));
}
- dblink_security_check(conn, rconn);
+ dblink_security_check(conn, rconn, connstr);
if (PQclientEncoding(conn) != GetDatabaseEncoding())
PQsetClientEncoding(conn, GetDatabaseEncodingName());
freeconn = true;
@@ -307,7 +309,7 @@ dblink_connect(PG_FUNCTION_ARGS)
}
/* check password actually used if not superuser */
- dblink_security_check(conn, rconn);
+ dblink_security_check(conn, rconn, connstr);
/* attempt to set client encoding to match server encoding, if needed */
if (PQclientEncoding(conn) != GetDatabaseEncoding())
@@ -2584,64 +2586,99 @@ deleteConnection(const char *name)
errmsg("undefined connection name")));
}
+/*
+ * We need to make sure that the connection made used credentials
+ * which were provided by the user, so check what credentials were
+ * used to connect and then make sure that they came from the user.
+ */
static void
-dblink_security_check(PGconn *conn, remoteConn *rconn)
+dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr)
{
- if (!superuser())
- {
- if (!PQconnectionUsedPassword(conn))
- {
- libpqsrv_disconnect(conn);
- if (rconn)
- pfree(rconn);
+ /* Superuser bypasses security check */
+ if (superuser())
+ return;
- 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.")));
- }
- }
+ /* If password was used to connect, make sure it was one provided */
+ if (PQconnectionUsedPassword(conn) && dblink_connstr_has_pw(connstr))
+ return;
+
+#ifdef ENABLE_GSS
+ /* If GSSAPI creds used to connect, make sure it was one delegated */
+ if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort))
+ return;
+#endif
+
+ /* Otherwise, fail out */
+ libpqsrv_disconnect(conn);
+ if (rconn)
+ pfree(rconn);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+ errmsg("password or GSSAPI delegated credentials required"),
+ errdetail("Non-superusers may only connect using credentials they provide, eg: password in connection string or delegated GSSAPI credentials"),
+ errhint("Ensure provided credentials match target server's authentication method.")));
}
/*
- * For non-superusers, insist that the connstr specify a password. This
- * prevents a password from being picked up from .pgpass, a service file,
- * the environment, etc. We don't want the postgres user's passwords
- * to be accessible to non-superusers.
+ * Function to check if the connection string includes an explicit
+ * password, needed to ensure that non-superuser password-based auth
+ * is using a provided password and not one picked up from the
+ * environment.
*/
-static void
-dblink_connstr_check(const char *connstr)
+static bool
+dblink_connstr_has_pw(const char *connstr)
{
- if (!superuser())
- {
- PQconninfoOption *options;
- PQconninfoOption *option;
- bool connstr_gives_password = false;
+ PQconninfoOption *options;
+ PQconninfoOption *option;
+ bool connstr_gives_password = false;
- options = PQconninfoParse(connstr, NULL);
- if (options)
+ options = PQconninfoParse(connstr, NULL);
+ if (options)
+ {
+ for (option = options; option->keyword != NULL; option++)
{
- for (option = options; option->keyword != NULL; option++)
+ if (strcmp(option->keyword, "password") == 0)
{
- if (strcmp(option->keyword, "password") == 0)
+ if (option->val != NULL && option->val[0] != '\0')
{
- if (option->val != NULL && option->val[0] != '\0')
- {
- connstr_gives_password = true;
- break;
- }
+ connstr_gives_password = true;
+ break;
}
}
- PQconninfoFree(options);
}
-
- if (!connstr_gives_password)
- ereport(ERROR,
- (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
- errmsg("password is required"),
- errdetail("Non-superusers must provide a password in the connection string.")));
+ PQconninfoFree(options);
}
+
+ return connstr_gives_password;
+}
+
+/*
+ * For non-superusers, insist that the connstr specify a password, except
+ * if GSSAPI credentials have been delegated (and we check that they are used
+ * for the connection in dblink_security_check later). This prevents a
+ * password or GSSAPI credentials from being picked up from .pgpass, a
+ * service file, the environment, etc. We don't want the postgres user's
+ * passwords or Kerberos credentials to be accessible to non-superusers.
+ */
+static void
+dblink_connstr_check(const char *connstr)
+{
+ if (superuser())
+ return;
+
+ if (dblink_connstr_has_pw(connstr))
+ return;
+
+#ifdef ENABLE_GSS
+ if (be_gssapi_get_deleg(MyProcPort))
+ return;
+#endif
+
+ ereport(ERROR,
+ (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+ errmsg("password or GSSAPI delegated credentials required"),
+ errdetail("Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.")));
}
/*
diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out
index 0f5050b4093..7809f58d96b 100644
--- a/contrib/dblink/expected/dblink.out
+++ b/contrib/dblink/expected/dblink.out
@@ -903,8 +903,8 @@ GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO regress_dblink_user;
SET SESSION AUTHORIZATION regress_dblink_user;
-- should fail
SELECT dblink_connect('myconn', 'fdtest');
-ERROR: password is required
-DETAIL: Non-superusers must provide a password in the connection string.
+ERROR: password or GSSAPI delegated credentials required
+DETAIL: Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.
-- should succeed
SELECT dblink_connect_u('myconn', 'fdtest');
dblink_connect_u
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 2969351e9a9..75d93d6eadf 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
#include "catalog/pg_user_mapping.h"
#include "commands/defrem.h"
#include "funcapi.h"
+#include "libpq/libpq-be.h"
#include "libpq/libpq-be-fe-helpers.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -149,6 +150,8 @@ static void pgfdw_finish_pre_subcommit_cleanup(List *pending_entries,
static void pgfdw_finish_abort_cleanup(List *pending_entries,
List *cancel_requested,
bool toplevel);
+static void pgfdw_security_check(const char **keywords, const char **values,
+ UserMapping *user, PGconn *conn);
static bool UserMappingPasswordRequired(UserMapping *user);
static bool disconnect_cached_connections(Oid serverid);
@@ -385,6 +388,47 @@ make_new_connection(ConnCacheEntry *entry, UserMapping *user)
}
/*
+ * Check that non-superuser has used password or delegated credentials
+ * to establish connection; otherwise, he's piggybacking on the
+ * postgres server's user identity. See also dblink_security_check()
+ * in contrib/dblink and check_conn_params.
+ */
+static void
+pgfdw_security_check(const char **keywords, const char **values, UserMapping *user, PGconn *conn)
+{
+ /* Superusers bypass the check */
+ if (superuser_arg(user->userid))
+ return;
+
+#ifdef ENABLE_GSS
+ /* Connected via GSSAPI with delegated credentials- all good. */
+ if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort))
+ return;
+#endif
+
+ /* Ok if superuser set PW required false. */
+ if (!UserMappingPasswordRequired(user))
+ return;
+
+ /* Connected via PW, with PW required true, and provided non-empty PW. */
+ if (PQconnectionUsedPassword(conn))
+ {
+ /* ok if params contain a non-empty password */
+ for (int i = 0; keywords[i] != NULL; i++)
+ {
+ if (strcmp(keywords[i], "password") == 0 && values[i][0] != '\0')
+ return;
+ }
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+ errmsg("password or GSSAPI delegated credentials required"),
+ errdetail("Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials."),
+ errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
+}
+
+/*
* Connect to remote server using specified server and user mapping properties.
*/
static PGconn *
@@ -495,19 +539,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
server->servername),
errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
- /*
- * Check that non-superuser has used password to establish connection;
- * otherwise, he's piggybacking on the postgres server's user
- * identity. See also dblink_security_check() in contrib/dblink and
- * check_conn_params.
- */
- if (!superuser_arg(user->userid) && UserMappingPasswordRequired(user) &&
- !PQconnectionUsedPassword(conn))
- 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 or password_required=false set in the user mapping attributes.")));
+ /* Perform post-connection security checks */
+ pgfdw_security_check(keywords, values, user, conn);
/* Prepare new session for use */
configure_remote_session(conn);
@@ -561,7 +594,8 @@ UserMappingPasswordRequired(UserMapping *user)
}
/*
- * For non-superusers, insist that the connstr specify a password. This
+ * For non-superusers, insist that the connstr specify a password or that the
+ * user provided their own GSSAPI delegated credentials. This
* prevents a password from being picked up from .pgpass, a service file, the
* environment, etc. We don't want the postgres user's passwords,
* certificates, etc to be accessible to non-superusers. (See also
@@ -576,6 +610,12 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
if (superuser_arg(user->userid))
return;
+#ifdef ENABLE_GSS
+ /* ok if the user provided their own delegated credentials */
+ if (be_gssapi_get_deleg(MyProcPort))
+ return;
+#endif
+
/* ok if params contain a non-empty password */
for (i = 0; keywords[i] != NULL; i++)
{
@@ -589,8 +629,8 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
ereport(ERROR,
(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
- errmsg("password is required"),
- errdetail("Non-superusers must provide a password in the user mapping.")));
+ errmsg("password or GSSAPI delegated credentials required"),
+ errdetail("Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.")));
}
/*
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 8f6a04f71b6..fd5752bd5bf 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -171,7 +171,8 @@ ALTER SERVER testserver1 OPTIONS (
sslcrl 'value',
--requirepeer 'value',
krbsrvname 'value',
- gsslib 'value'
+ gsslib 'value',
+ gssdeleg 'value'
--replication 'value'
);
-- Error, invalid list syntax
@@ -9840,8 +9841,8 @@ CREATE FOREIGN TABLE pg_temp.ft1_nopw (
c8 user_enum
) SERVER loopback_nopw OPTIONS (schema_name 'public', table_name 'ft1');
SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR: password is required
-DETAIL: Non-superusers must provide a password in the user mapping.
+ERROR: password or GSSAPI delegated credentials required
+DETAIL: Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.
-- If we add a password to the connstr it'll fail, because we don't allow passwords
-- in connstrs only in user mappings.
ALTER SERVER loopback_nopw OPTIONS (ADD password 'dummypw');
@@ -9853,16 +9854,16 @@ HINT: Perhaps you meant the option "passfile".
-- This won't work with installcheck, but neither will most of the FDW checks.
ALTER USER MAPPING FOR CURRENT_USER SERVER loopback_nopw OPTIONS (ADD password 'dummypw');
SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR: password is required
-DETAIL: Non-superuser cannot connect if the server does not request a password.
+ERROR: password or GSSAPI delegated credentials required
+DETAIL: Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials.
HINT: Target server's authentication method must be changed or password_required=false set in the user mapping attributes.
-- Unpriv user cannot make the mapping passwordless
ALTER USER MAPPING FOR CURRENT_USER SERVER loopback_nopw OPTIONS (ADD password_required 'false');
ERROR: password_required=false is superuser-only
HINT: User mappings with the password_required option set to false may only be created or modified by the superuser.
SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR: password is required
-DETAIL: Non-superuser cannot connect if the server does not request a password.
+ERROR: password or GSSAPI delegated credentials required
+DETAIL: Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials.
HINT: Target server's authentication method must be changed or password_required=false set in the user mapping attributes.
RESET ROLE;
-- But the superuser can
@@ -9890,8 +9891,8 @@ DROP USER MAPPING FOR CURRENT_USER SERVER loopback_nopw;
-- This will fail again as it'll resolve the user mapping for public, which
-- lacks password_required=false
SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR: password is required
-DETAIL: Non-superusers must provide a password in the user mapping.
+ERROR: password or GSSAPI delegated credentials required
+DETAIL: Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.
RESET ROLE;
-- The user mapping for public is passwordless and lacks the password_required=false
-- mapping option, but will work because the current user is a superuser.
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 4229d2048c3..fe40d50c6dd 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -288,6 +288,12 @@ InitPgFdwOptions(void)
{"sslcert", UserMappingRelationId, true},
{"sslkey", UserMappingRelationId, true},
+ /*
+ * gssdeleg is also a libpq option but should be allowed in a user
+ * mapping context too
+ */
+ {"gssdeleg", UserMappingRelationId, true},
+
{NULL, InvalidOid, false}
};
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 5bd69339dfc..c05046f8676 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -185,7 +185,8 @@ ALTER SERVER testserver1 OPTIONS (
sslcrl 'value',
--requirepeer 'value',
krbsrvname 'value',
- gsslib 'value'
+ gsslib 'value',
+ gssdeleg 'value'
--replication 'value'
);