Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3a465cc

Browse filesBrowse files
committed
libpq: Add support for require_auth to control authorized auth methods
The new connection parameter require_auth allows a libpq client to define a list of comma-separated acceptable authentication types for use with the server. There is no negotiation: if the server does not present one of the allowed authentication requests, the connection attempt done by the client fails. The following keywords can be defined in the list: - password, for AUTH_REQ_PASSWORD. - md5, for AUTH_REQ_MD5. - gss, for AUTH_REQ_GSS[_CONT]. - sspi, for AUTH_REQ_SSPI and AUTH_REQ_GSS_CONT. - scram-sha-256, for AUTH_REQ_SASL[_CONT|_FIN]. - creds, for AUTH_REQ_SCM_CREDS (perhaps this should be removed entirely now). - none, to control unauthenticated connections. All the methods that can be defined in the list can be negated, like "!password", in which case the server must NOT use the listed authentication type. The special method "none" allows/disallows the use of unauthenticated connections (but it does not govern transport-level authentication via TLS or GSSAPI). Internally, the patch logic is tied to check_expected_areq(), that was used for channel_binding, ensuring that an incoming request is compatible with conn->require_auth. It also introduces a new flag, conn->client_finished_auth, which is set by various authentication routines when the client side of the handshake is finished. This signals to check_expected_areq() that an AUTH_REQ_OK from the server is expected, and allows the client to complain if the server bypasses authentication entirely, with for example the reception of a too-early AUTH_REQ_OK message. Regression tests are added in authentication TAP tests for all the keywords supported (except "creds", because it is around only for compatibility reasons). A new TAP script has been added for SSPI, as there was no script dedicated to it yet. It relies on SSPI being the default authentication method on Windows, as set by pg_regress. Author: Jacob Champion Reviewed-by: Peter Eisentraut, David G. Johnston, Michael Paquier Discussion: https://postgr.es/m/9e5a8ccddb8355ea9fa4b75a1e3a9edc88a70cd3.camel@vmware.com
1 parent 7274009 commit 3a465cc
Copy full SHA for 3a465cc

File tree

12 files changed

+779
-0
lines changed
Filter options

12 files changed

+779
-0
lines changed

‎doc/src/sgml/libpq.sgml

Copy file name to clipboardExpand all lines: doc/src/sgml/libpq.sgml
+115Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,111 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
12201220
</listitem>
12211221
</varlistentry>
12221222

1223+
<varlistentry id="libpq-connect-require-auth" xreflabel="require_auth">
1224+
<term><literal>require_auth</literal></term>
1225+
<listitem>
1226+
<para>
1227+
Specifies the authentication method that the client requires from the
1228+
server. If the server does not use the required method to authenticate
1229+
the client, or if the authentication handshake is not fully completed by
1230+
the server, the connection will fail. A comma-separated list of methods
1231+
may also be provided, of which the server must use exactly one in order
1232+
for the connection to succeed. By default, any authentication method is
1233+
accepted, and the server is free to skip authentication altogether.
1234+
</para>
1235+
<para>
1236+
Methods may be negated with the addition of a <literal>!</literal>
1237+
prefix, in which case the server must <emphasis>not</emphasis> attempt
1238+
the listed method; any other method is accepted, and the server is free
1239+
not to authenticate the client at all. If a comma-separated list is
1240+
provided, the server may not attempt <emphasis>any</emphasis> of the
1241+
listed negated methods. Negated and non-negated forms may not be
1242+
combined in the same setting.
1243+
</para>
1244+
<para>
1245+
As a final special case, the <literal>none</literal> method requires the
1246+
server not to use an authentication challenge. (It may also be negated,
1247+
to require some form of authentication.)
1248+
</para>
1249+
<para>
1250+
The following methods may be specified:
1251+
1252+
<variablelist>
1253+
<varlistentry>
1254+
<term><literal>password</literal></term>
1255+
<listitem>
1256+
<para>
1257+
The server must request plaintext password authentication.
1258+
</para>
1259+
</listitem>
1260+
</varlistentry>
1261+
1262+
<varlistentry>
1263+
<term><literal>md5</literal></term>
1264+
<listitem>
1265+
<para>
1266+
The server must request MD5 hashed password authentication.
1267+
</para>
1268+
</listitem>
1269+
</varlistentry>
1270+
1271+
<varlistentry>
1272+
<term><literal>gss</literal></term>
1273+
<listitem>
1274+
<para>
1275+
The server must either request a Kerberos handshake via
1276+
<acronym>GSSAPI</acronym> or establish a
1277+
<acronym>GSS</acronym>-encrypted channel (see also
1278+
<xref linkend="libpq-connect-gssencmode" />).
1279+
</para>
1280+
</listitem>
1281+
</varlistentry>
1282+
1283+
<varlistentry>
1284+
<term><literal>sspi</literal></term>
1285+
<listitem>
1286+
<para>
1287+
The server must request Windows <acronym>SSPI</acronym>
1288+
authentication.
1289+
</para>
1290+
</listitem>
1291+
</varlistentry>
1292+
1293+
<varlistentry>
1294+
<term><literal>scram-sha-256</literal></term>
1295+
<listitem>
1296+
<para>
1297+
The server must successfully complete a SCRAM-SHA-256 authentication
1298+
exchange with the client.
1299+
</para>
1300+
</listitem>
1301+
</varlistentry>
1302+
1303+
<varlistentry>
1304+
<term><literal>creds</literal></term>
1305+
<listitem>
1306+
<para>
1307+
The server must request SCM credential authentication (deprecated
1308+
as of <productname>PostgreSQL</productname> 9.1).
1309+
</para>
1310+
</listitem>
1311+
</varlistentry>
1312+
1313+
<varlistentry>
1314+
<term><literal>none</literal></term>
1315+
<listitem>
1316+
<para>
1317+
The server must not prompt the client for an authentication
1318+
exchange. (This does not prohibit client certificate authentication
1319+
via TLS, nor GSS authentication via its encrypted transport.)
1320+
</para>
1321+
</listitem>
1322+
</varlistentry>
1323+
</variablelist>
1324+
</para>
1325+
</listitem>
1326+
</varlistentry>
1327+
12231328
<varlistentry id="libpq-connect-channel-binding" xreflabel="channel_binding">
12241329
<term><literal>channel_binding</literal></term>
12251330
<listitem>
@@ -7774,6 +7879,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
77747879
</para>
77757880
</listitem>
77767881

7882+
<listitem>
7883+
<para>
7884+
<indexterm>
7885+
<primary><envar>PGREQUIREAUTH</envar></primary>
7886+
</indexterm>
7887+
<envar>PGREQUIREAUTH</envar> behaves the same as the <xref
7888+
linkend="libpq-connect-require-auth"/> connection parameter.
7889+
</para>
7890+
</listitem>
7891+
77777892
<listitem>
77787893
<para>
77797894
<indexterm>

‎src/include/libpq/pqcomm.h

Copy file name to clipboardExpand all lines: src/include/libpq/pqcomm.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ extern PGDLLIMPORT bool Db_user_namespace;
123123
#define AUTH_REQ_SASL 10 /* Begin SASL authentication */
124124
#define AUTH_REQ_SASL_CONT 11 /* Continue SASL authentication */
125125
#define AUTH_REQ_SASL_FIN 12 /* Final SASL message */
126+
#define AUTH_REQ_MAX AUTH_REQ_SASL_FIN /* maximum AUTH_REQ_* value */
126127

127128
typedef uint32 AuthRequest;
128129

‎src/interfaces/libpq/fe-auth-scram.c

Copy file name to clipboardExpand all lines: src/interfaces/libpq/fe-auth-scram.c
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ scram_exchange(void *opaq, char *input, int inputlen,
282282
}
283283
*done = true;
284284
state->state = FE_SCRAM_FINISHED;
285+
state->conn->client_finished_auth = true;
285286
break;
286287

287288
default:

‎src/interfaces/libpq/fe-auth.c

Copy file name to clipboardExpand all lines: src/interfaces/libpq/fe-auth.c
+139Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,10 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
136136
}
137137

138138
if (maj_stat == GSS_S_COMPLETE)
139+
{
140+
conn->client_finished_auth = true;
139141
gss_release_name(&lmin_s, &conn->gtarg_nam);
142+
}
140143

141144
return STATUS_OK;
142145
}
@@ -321,6 +324,9 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
321324
FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
322325
}
323326

327+
if (r == SEC_E_OK)
328+
conn->client_finished_auth = true;
329+
324330
/* Cleanup is handled by the code in freePGconn() */
325331
return STATUS_OK;
326332
}
@@ -735,6 +741,8 @@ pg_local_sendauth(PGconn *conn)
735741
strerror_r(errno, sebuf, sizeof(sebuf)));
736742
return STATUS_ERROR;
737743
}
744+
745+
conn->client_finished_auth = true;
738746
return STATUS_OK;
739747
#else
740748
libpq_append_conn_error(conn, "SCM_CRED authentication method not supported");
@@ -805,6 +813,41 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
805813
return ret;
806814
}
807815

816+
/*
817+
* Translate a disallowed AuthRequest code into an error message.
818+
*/
819+
static const char *
820+
auth_method_description(AuthRequest areq)
821+
{
822+
switch (areq)
823+
{
824+
case AUTH_REQ_PASSWORD:
825+
return libpq_gettext("server requested a cleartext password");
826+
case AUTH_REQ_MD5:
827+
return libpq_gettext("server requested a hashed password");
828+
case AUTH_REQ_GSS:
829+
case AUTH_REQ_GSS_CONT:
830+
return libpq_gettext("server requested GSSAPI authentication");
831+
case AUTH_REQ_SSPI:
832+
return libpq_gettext("server requested SSPI authentication");
833+
case AUTH_REQ_SCM_CREDS:
834+
return libpq_gettext("server requested UNIX socket credentials");
835+
case AUTH_REQ_SASL:
836+
case AUTH_REQ_SASL_CONT:
837+
case AUTH_REQ_SASL_FIN:
838+
return libpq_gettext("server requested SASL authentication");
839+
}
840+
841+
return libpq_gettext("server requested an unknown authentication type");
842+
}
843+
844+
/*
845+
* Convenience macro for checking the allowed_auth_methods bitmask. Caller
846+
* must ensure that type is not greater than 31 (high bit of the bitmask).
847+
*/
848+
#define auth_method_allowed(conn, type) \
849+
(((conn)->allowed_auth_methods & (1 << (type))) != 0)
850+
808851
/*
809852
* Verify that the authentication request is expected, given the connection
810853
* parameters. This is especially important when the client wishes to
@@ -814,6 +857,99 @@ static bool
814857
check_expected_areq(AuthRequest areq, PGconn *conn)
815858
{
816859
bool result = true;
860+
const char *reason = NULL;
861+
862+
StaticAssertDecl((sizeof(conn->allowed_auth_methods) * CHAR_BIT) > AUTH_REQ_MAX,
863+
"AUTH_REQ_MAX overflows the allowed_auth_methods bitmask");
864+
865+
/*
866+
* If the user required a specific auth method, or specified an allowed
867+
* set, then reject all others here, and make sure the server actually
868+
* completes an authentication exchange.
869+
*/
870+
if (conn->require_auth)
871+
{
872+
switch (areq)
873+
{
874+
case AUTH_REQ_OK:
875+
876+
/*
877+
* Check to make sure we've actually finished our exchange (or
878+
* else that the user has allowed an authentication-less
879+
* connection).
880+
*
881+
* If the user has allowed both SCRAM and unauthenticated
882+
* (trust) connections, then this check will silently accept
883+
* partial SCRAM exchanges, where a misbehaving server does
884+
* not provide its verifier before sending an OK. This is
885+
* consistent with historical behavior, but it may be a point
886+
* to revisit in the future, since it could allow a server
887+
* that doesn't know the user's password to silently harvest
888+
* material for a brute force attack.
889+
*/
890+
if (!conn->auth_required || conn->client_finished_auth)
891+
break;
892+
893+
/*
894+
* No explicit authentication request was made by the server
895+
* -- or perhaps it was made and not completed, in the case of
896+
* SCRAM -- but there is one special case to check. If the
897+
* user allowed "gss", then a GSS-encrypted channel also
898+
* satisfies the check.
899+
*/
900+
#ifdef ENABLE_GSS
901+
if (auth_method_allowed(conn, AUTH_REQ_GSS) && conn->gssenc)
902+
{
903+
/*
904+
* If implicit GSS auth has already been performed via GSS
905+
* encryption, we don't need to have performed an
906+
* AUTH_REQ_GSS exchange. This allows require_auth=gss to
907+
* be combined with gssencmode, since there won't be an
908+
* explicit authentication request in that case.
909+
*/
910+
}
911+
else
912+
#endif
913+
{
914+
reason = libpq_gettext("server did not complete authentication");
915+
result = false;
916+
}
917+
918+
break;
919+
920+
case AUTH_REQ_PASSWORD:
921+
case AUTH_REQ_MD5:
922+
case AUTH_REQ_GSS:
923+
case AUTH_REQ_GSS_CONT:
924+
case AUTH_REQ_SSPI:
925+
case AUTH_REQ_SCM_CREDS:
926+
case AUTH_REQ_SASL:
927+
case AUTH_REQ_SASL_CONT:
928+
case AUTH_REQ_SASL_FIN:
929+
930+
/*
931+
* We don't handle these with the default case, to avoid
932+
* bit-shifting past the end of the allowed_auth_methods mask
933+
* if the server sends an unexpected AuthRequest.
934+
*/
935+
result = auth_method_allowed(conn, areq);
936+
break;
937+
938+
default:
939+
result = false;
940+
break;
941+
}
942+
}
943+
944+
if (!result)
945+
{
946+
if (!reason)
947+
reason = auth_method_description(areq);
948+
949+
libpq_append_conn_error(conn, "auth method \"%s\" requirement failed: %s",
950+
conn->require_auth, reason);
951+
return result;
952+
}
817953

818954
/*
819955
* When channel_binding=require, we must protect against two cases: (1) we
@@ -1008,6 +1144,9 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
10081144
"fe_sendauth: error sending password authentication\n");
10091145
return STATUS_ERROR;
10101146
}
1147+
1148+
/* We expect no further authentication requests. */
1149+
conn->client_finished_auth = true;
10111150
break;
10121151
}
10131152

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.