aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-auth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/libpq/fe-auth.c')
-rw-r--r--src/interfaces/libpq/fe-auth.c139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index ab454e6cd02..a3b80dc550f 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -136,7 +136,10 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
}
if (maj_stat == GSS_S_COMPLETE)
+ {
+ conn->client_finished_auth = true;
gss_release_name(&lmin_s, &conn->gtarg_nam);
+ }
return STATUS_OK;
}
@@ -321,6 +324,9 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
}
+ if (r == SEC_E_OK)
+ conn->client_finished_auth = true;
+
/* Cleanup is handled by the code in freePGconn() */
return STATUS_OK;
}
@@ -735,6 +741,8 @@ pg_local_sendauth(PGconn *conn)
strerror_r(errno, sebuf, sizeof(sebuf)));
return STATUS_ERROR;
}
+
+ conn->client_finished_auth = true;
return STATUS_OK;
#else
libpq_append_conn_error(conn, "SCM_CRED authentication method not supported");
@@ -806,6 +814,41 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
}
/*
+ * Translate a disallowed AuthRequest code into an error message.
+ */
+static const char *
+auth_method_description(AuthRequest areq)
+{
+ switch (areq)
+ {
+ case AUTH_REQ_PASSWORD:
+ return libpq_gettext("server requested a cleartext password");
+ case AUTH_REQ_MD5:
+ return libpq_gettext("server requested a hashed password");
+ case AUTH_REQ_GSS:
+ case AUTH_REQ_GSS_CONT:
+ return libpq_gettext("server requested GSSAPI authentication");
+ case AUTH_REQ_SSPI:
+ return libpq_gettext("server requested SSPI authentication");
+ case AUTH_REQ_SCM_CREDS:
+ return libpq_gettext("server requested UNIX socket credentials");
+ case AUTH_REQ_SASL:
+ case AUTH_REQ_SASL_CONT:
+ case AUTH_REQ_SASL_FIN:
+ return libpq_gettext("server requested SASL authentication");
+ }
+
+ return libpq_gettext("server requested an unknown authentication type");
+}
+
+/*
+ * Convenience macro for checking the allowed_auth_methods bitmask. Caller
+ * must ensure that type is not greater than 31 (high bit of the bitmask).
+ */
+#define auth_method_allowed(conn, type) \
+ (((conn)->allowed_auth_methods & (1 << (type))) != 0)
+
+/*
* Verify that the authentication request is expected, given the connection
* parameters. This is especially important when the client wishes to
* authenticate the server before any sensitive information is exchanged.
@@ -814,6 +857,99 @@ static bool
check_expected_areq(AuthRequest areq, PGconn *conn)
{
bool result = true;
+ const char *reason = NULL;
+
+ StaticAssertDecl((sizeof(conn->allowed_auth_methods) * CHAR_BIT) > AUTH_REQ_MAX,
+ "AUTH_REQ_MAX overflows the allowed_auth_methods bitmask");
+
+ /*
+ * If the user required a specific auth method, or specified an allowed
+ * set, then reject all others here, and make sure the server actually
+ * completes an authentication exchange.
+ */
+ if (conn->require_auth)
+ {
+ switch (areq)
+ {
+ case AUTH_REQ_OK:
+
+ /*
+ * Check to make sure we've actually finished our exchange (or
+ * else that the user has allowed an authentication-less
+ * connection).
+ *
+ * If the user has allowed both SCRAM and unauthenticated
+ * (trust) connections, then this check will silently accept
+ * partial SCRAM exchanges, where a misbehaving server does
+ * not provide its verifier before sending an OK. This is
+ * consistent with historical behavior, but it may be a point
+ * to revisit in the future, since it could allow a server
+ * that doesn't know the user's password to silently harvest
+ * material for a brute force attack.
+ */
+ if (!conn->auth_required || conn->client_finished_auth)
+ break;
+
+ /*
+ * No explicit authentication request was made by the server
+ * -- or perhaps it was made and not completed, in the case of
+ * SCRAM -- but there is one special case to check. If the
+ * user allowed "gss", then a GSS-encrypted channel also
+ * satisfies the check.
+ */
+#ifdef ENABLE_GSS
+ if (auth_method_allowed(conn, AUTH_REQ_GSS) && conn->gssenc)
+ {
+ /*
+ * If implicit GSS auth has already been performed via GSS
+ * encryption, we don't need to have performed an
+ * AUTH_REQ_GSS exchange. This allows require_auth=gss to
+ * be combined with gssencmode, since there won't be an
+ * explicit authentication request in that case.
+ */
+ }
+ else
+#endif
+ {
+ reason = libpq_gettext("server did not complete authentication");
+ result = false;
+ }
+
+ break;
+
+ case AUTH_REQ_PASSWORD:
+ case AUTH_REQ_MD5:
+ case AUTH_REQ_GSS:
+ case AUTH_REQ_GSS_CONT:
+ case AUTH_REQ_SSPI:
+ case AUTH_REQ_SCM_CREDS:
+ case AUTH_REQ_SASL:
+ case AUTH_REQ_SASL_CONT:
+ case AUTH_REQ_SASL_FIN:
+
+ /*
+ * We don't handle these with the default case, to avoid
+ * bit-shifting past the end of the allowed_auth_methods mask
+ * if the server sends an unexpected AuthRequest.
+ */
+ result = auth_method_allowed(conn, areq);
+ break;
+
+ default:
+ result = false;
+ break;
+ }
+ }
+
+ if (!result)
+ {
+ if (!reason)
+ reason = auth_method_description(areq);
+
+ libpq_append_conn_error(conn, "auth method \"%s\" requirement failed: %s",
+ conn->require_auth, reason);
+ return result;
+ }
/*
* 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)
"fe_sendauth: error sending password authentication\n");
return STATUS_ERROR;
}
+
+ /* We expect no further authentication requests. */
+ conn->client_finished_auth = true;
break;
}