Authentification method on client side checking
Hello,
Despite the addition of SCRAM authentification to PostgreSQL 10, MITM
attack can be performed by saying that the server supports, for example,
only md5 authentication. The possible solution for it is checking
authentification method on a client side and reject connections that
could be unsafe.
Postgresql server can require unencrypted password passing, md5, scram,
gss or sspi authentification.
In the attached patch you can find the solution for it. The new provided
features are the following:
The parameter with acceptable authentification methods can be passed
into connection methods of libpq library.
Also, this parameter can be specified to psql as a command line
argument.
The documentation for command line arguments of psql and arguments of
libpq methods are also presented.
Thank you for attention!
Best,
--
------
Victor Drobny
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
authentification_method_on_client_check.patchtext/x-diff; name=authentification_method_on_client_check.patchDownload
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 8068a28..1877b2d 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -75,6 +75,7 @@ struct adhoc_opts
bool no_psqlrc;
bool single_txn;
bool list_dbs;
+ char *authtype;
SimpleActionList actions;
};
@@ -213,7 +214,7 @@ main(int argc, char *argv[])
/* loop until we have a password if requested by backend */
do
{
-#define PARAMS_ARRAY_SIZE 8
+#define PARAMS_ARRAY_SIZE 9
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
@@ -232,8 +233,10 @@ main(int argc, char *argv[])
values[5] = pset.progname;
keywords[6] = "client_encoding";
values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto";
- keywords[7] = NULL;
- values[7] = NULL;
+ keywords[7] = "acc_auth";
+ values[7] = options.authtype;
+ keywords[8] = NULL;
+ values[8] = NULL;
new_pass = false;
pset.db = PQconnectdbParams(keywords, values, true);
@@ -441,6 +444,7 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
{"single-line", no_argument, NULL, 'S'},
{"tuples-only", no_argument, NULL, 't'},
{"table-attr", required_argument, NULL, 'T'},
+ {"authtype", required_argument, NULL, 'u'},
{"username", required_argument, NULL, 'U'},
{"set", required_argument, NULL, 'v'},
{"variable", required_argument, NULL, 'v'},
@@ -458,7 +462,7 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
memset(options, 0, sizeof *options);
- while ((c = getopt_long(argc, argv, "aAbc:d:eEf:F:h:HlL:no:p:P:qR:sStT:U:v:VwWxXz?01",
+ while ((c = getopt_long(argc, argv, "aAbc:d:eEf:F:h:HlL:no:p:P:qR:sStT:u:U:v:VwWxXz?01",
long_options, &optindex)) != -1)
{
switch (c)
@@ -566,6 +570,9 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
case 'T':
pset.popt.topt.tableAttr = pg_strdup(optarg);
break;
+ case 'u':
+ options->authtype = pg_strdup(optarg);
+ break;
case 'U':
options->username = pg_strdup(optarg);
break;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 4dc8924..b8e77d4 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -304,6 +304,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
offsetof(struct pg_conn, target_session_attrs)},
+ {"acc_auth", NULL, NULL, NULL,
+ "Acceptable authentification methods", "", 20,
+ offsetof(struct pg_conn, acc_auth)},
+
/* Terminating entry --- MUST BE LAST */
{NULL, NULL, NULL, NULL,
NULL, NULL, 0}
@@ -1000,6 +1004,43 @@ connectOptions2(PGconn *conn)
}
}
+ /* Validate acceptable authentification methods */
+ if (conn->acc_auth)
+ {
+ char * pvalue = conn->acc_auth;
+ char * comma = pvalue;
+ while ((comma = strchr(pvalue, ',')))
+ {
+ *comma = '\0';
+ if (strcmp(pvalue, "password") != 0
+ && strcmp(pvalue, "md5") != 0
+ && strcmp(pvalue, "scram") != 0
+ && strcmp(pvalue, "gss") != 0
+ && strcmp(pvalue, "sspi") != 0)
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid authtype value: \"%s\"\n"),
+ pvalue);
+ return false;
+ }
+ *comma = ',';
+ pvalue = comma + 1;
+ }
+ if (strcmp(pvalue, "password") != 0
+ && strcmp(pvalue, "md5") != 0
+ && strcmp(pvalue, "scram") != 0
+ && strcmp(pvalue, "gss") != 0
+ && strcmp(pvalue, "sspi") != 0)
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid authtype value: \"%s\"\n"),
+ pvalue);
+ return false;
+ }
+ }
+
/*
* validate sslmode option
*/
@@ -1822,6 +1863,43 @@ restoreErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
termPQExpBuffer(savedMessage);
}
+static bool
+isAllowableAuth(int areq, char* acc_auth)
+{
+ /* Allowable authentification scheme is not specified. */
+ if (acc_auth == 0)
+ return true;
+ switch (areq)
+ {
+ case AUTH_REQ_OK:
+ return true;
+ case AUTH_REQ_KRB4:
+ return false; // Since it is not supported anymore
+ case AUTH_REQ_KRB5:
+ return false; // Since it is not supported anymore
+ case AUTH_REQ_PASSWORD:
+ return strstr(acc_auth, "password") != NULL;
+ case AUTH_REQ_CRYPT:
+ return false; // Since it is not supported anymore
+ case AUTH_REQ_MD5:
+ return strstr(acc_auth, "md5") != NULL;
+ case AUTH_REQ_SCM_CREDS:
+ break;
+ case AUTH_REQ_GSS:
+ return strstr(acc_auth, "gss") != NULL;
+ case AUTH_REQ_GSS_CONT:
+ return strstr(acc_auth, "gss") != NULL
+ || strstr(acc_auth, "sspi") != NULL;
+ case AUTH_REQ_SSPI:
+ return strstr(acc_auth, "sspi") != NULL;
+ case AUTH_REQ_SASL:
+ case AUTH_REQ_SASL_CONT:
+ case AUTH_REQ_SASL_FIN:
+ return strstr(acc_auth, "scram") != NULL;
+ }
+ return true;
+}
+
/* ----------------
* PQconnectPoll
*
@@ -2680,6 +2758,13 @@ keep_going: /* We will come back to here until there is
}
msgLength -= 4;
+ /* Check if authentification method is allowedable */
+ if (!isAllowableAuth(areq, conn->acc_auth))
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("Authentification scheme is not allowed\n"));
+ goto error_return;
+ }
/*
* Ensure the password salt is in the input buffer, if it's an
* MD5 request. All the other authentication methods that
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 34d0492..ce55951 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -488,6 +488,8 @@ struct pg_conn
* connection */
#endif
+ char *acc_auth; /* Acceptable authentification methods */
+
/* Buffer for current error message */
PQExpBufferData errorMessage; /* expansible string */
On 09/07/17 18:47, Victor Drobny wrote:
Hello,
Despite the addition of SCRAM authentification to PostgreSQL 10, MITM
attack can be performed by saying that the server supports, for
example, only md5 authentication. The possible solution for it is
checking authentification method on a client side and reject
connections that could be unsafe.Postgresql server can require unencrypted password passing, md5,
scram, gss or sspi authentification.
Hi Victor.
Precisely yesterday I initiated a similar thread:
/messages/by-id/d4098ef4-2910-c8bf-f1e3-f178ba77c381@8kdata.com
I think that a) the mere auth mechanism is not enough (channel
binding or not, ssl or not, change a lot the effective security
obtained) and b) maybe a categorization is a better way of specifying a
connection security requirements.
What's your opinion on this? Any answer should also be coordinated
among the drivers.
�lvaro
--
�lvaro Hern�ndez Tortosa
-----------
<8K>data
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Mon, Jul 10, 2017 at 9:29 AM, Álvaro Hernández Tortosa
<aht@8kdata.com> wrote:
Precisely yesterday I initiated a similar thread:
/messages/by-id/d4098ef4-2910-c8bf-f1e3-f178ba77c381@8kdata.comI think that a) the mere auth mechanism is not enough (channel binding
or not, ssl or not, change a lot the effective security obtained) and b)
maybe a categorization is a better way of specifying a connection security
requirements.What's your opinion on this? Any answer should also be coordinated among
the drivers.
Before rushing into implementing something that we may not want, let's
discuss the matter on the thread spawned by Álvaro and find an
agreement and a direction of implementation. I was planning to answer
your message with my own thoughts on the matter. Having more control
in libpq is definitely something that we should have.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers