summaryrefslogtreecommitdiff
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.c158
1 files changed, 106 insertions, 52 deletions
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 14e00a69e2a..d81ee4f9447 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -475,88 +475,129 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)
static int
pg_SASL_init(PGconn *conn, int payloadlen)
{
- char auth_mechanism[21];
- char *initialresponse;
+ char *initialresponse = NULL;
int initialresponselen;
bool done;
bool success;
- int res;
+ const char *selected_mechanism;
+ PQExpBufferData mechanism_buf;
- /*
- * Read the authentication mechanism the server told us to use.
- */
- if (payloadlen > sizeof(auth_mechanism) - 1)
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SASL authentication mechanism not supported\n"));
- if (pqGetnchar(auth_mechanism, payloadlen, conn))
+ initPQExpBuffer(&mechanism_buf);
+
+ if (conn->sasl_state)
{
printfPQExpBuffer(&conn->errorMessage,
- "fe_sendauth: invalid authentication request from server: invalid authentication mechanism\n");
-
- return STATUS_ERROR;
+ libpq_gettext("duplicate SASL authentication request\n"));
+ goto error;
}
- auth_mechanism[payloadlen] = '\0';
/*
- * Check the authentication mechanism (only SCRAM-SHA-256 is supported at
- * the moment.)
+ * Parse the list of SASL authentication mechanisms in the
+ * AuthenticationSASL message, and select the best mechanism that we
+ * support. (Only SCRAM-SHA-256 is supported at the moment.)
*/
- if (strcmp(auth_mechanism, SCRAM_SHA256_NAME) == 0)
+ selected_mechanism = NULL;
+ for (;;)
{
- char *password;
-
- conn->password_needed = true;
- password = conn->connhost[conn->whichhost].password;
- if (password == NULL)
- password = conn->pgpass;
- if (password == NULL || password[0] == '\0')
+ if (pqGets(&mechanism_buf, conn))
{
printfPQExpBuffer(&conn->errorMessage,
- PQnoPasswordSupplied);
- return STATUS_ERROR;
+ "fe_sendauth: invalid authentication request from server: invalid list of authentication mechanisms\n");
+ goto error;
}
+ if (PQExpBufferDataBroken(mechanism_buf))
+ goto oom_error;
+
+ /* An empty string indicates end of list */
+ if (mechanism_buf.data[0] == '\0')
+ break;
- conn->sasl_state = pg_fe_scram_init(conn->pguser, password);
- if (!conn->sasl_state)
+ /*
+ * If we have already selected a mechanism, just skip through the rest
+ * of the list.
+ */
+ if (selected_mechanism)
+ continue;
+
+ /*
+ * Do we support this mechanism?
+ */
+ if (strcmp(mechanism_buf.data, SCRAM_SHA256_NAME) == 0)
{
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return STATUS_ERROR;
+ char *password;
+
+ conn->password_needed = true;
+ password = conn->connhost[conn->whichhost].password;
+ if (password == NULL)
+ password = conn->pgpass;
+ if (password == NULL || password[0] == '\0')
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ PQnoPasswordSupplied);
+ goto error;
+ }
+
+ conn->sasl_state = pg_fe_scram_init(conn->pguser, password);
+ if (!conn->sasl_state)
+ goto oom_error;
+ selected_mechanism = SCRAM_SHA256_NAME;
}
}
- else
+
+ if (!selected_mechanism)
{
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SASL authentication mechanism %s not supported\n"),
- auth_mechanism);
- return STATUS_ERROR;
+ libpq_gettext("none of the server's SASL authentication mechanisms are supported\n"));
+ goto error;
}
- /* Send the initial client response */
+ /* Get the mechanism-specific Initial Client Response, if any */
pg_fe_scram_exchange(conn->sasl_state,
NULL, -1,
&initialresponse, &initialresponselen,
&done, &success, &conn->errorMessage);
+ if (done && !success)
+ goto error;
+
+ /*
+ * Build a SASLInitialResponse message, and send it.
+ */
+ if (pqPutMsgStart('p', true, conn))
+ goto error;
+ if (pqPuts(selected_mechanism, conn))
+ goto error;
if (initialresponse)
{
- res = pqPacketSend(conn, 'p', initialresponse, initialresponselen);
- free(initialresponse);
-
- if (res != STATUS_OK)
- return STATUS_ERROR;
+ if (pqPutInt(initialresponselen, 4, conn))
+ goto error;
+ if (pqPutnchar(initialresponse, initialresponselen, conn))
+ goto error;
}
+ if (pqPutMsgEnd(conn))
+ goto error;
+ if (pqFlush(conn))
+ goto error;
- if (done && !success)
- {
- /* Use error message, if set already */
- if (conn->errorMessage.len == 0)
- printfPQExpBuffer(&conn->errorMessage,
- "fe_sendauth: error in SASL authentication\n");
- return STATUS_ERROR;
- }
+ termPQExpBuffer(&mechanism_buf);
+ if (initialresponse)
+ free(initialresponse);
return STATUS_OK;
+
+error:
+ termPQExpBuffer(&mechanism_buf);
+ if (initialresponse)
+ free(initialresponse);
+ return STATUS_ERROR;
+
+oom_error:
+ termPQExpBuffer(&mechanism_buf);
+ if (initialresponse)
+ free(initialresponse);
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return STATUS_ERROR;
}
/*
@@ -565,7 +606,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
* the protocol.
*/
static int
-pg_SASL_continue(PGconn *conn, int payloadlen)
+pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
{
char *output;
int outputlen;
@@ -598,9 +639,20 @@ pg_SASL_continue(PGconn *conn, int payloadlen)
&done, &success, &conn->errorMessage);
free(challenge); /* don't need the input anymore */
- /* Send the SASL response to the server, if any. */
+ if (final && !done)
+ {
+ if (outputlen != 0)
+ free(output);
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"));
+ return STATUS_ERROR;
+ }
if (outputlen != 0)
{
+ /*
+ * Send the SASL response to the server.
+ */
res = pqPacketSend(conn, 'p', output, outputlen);
free(output);
@@ -918,13 +970,15 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
break;
case AUTH_REQ_SASL_CONT:
+ case AUTH_REQ_SASL_FIN:
if (conn->sasl_state == NULL)
{
printfPQExpBuffer(&conn->errorMessage,
"fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without AUTH_REQ_SASL\n");
return STATUS_ERROR;
}
- if (pg_SASL_continue(conn, payloadlen) != STATUS_OK)
+ if (pg_SASL_continue(conn, payloadlen,
+ (areq == AUTH_REQ_SASL_FIN)) != STATUS_OK)
{
/* Use error message, if set already */
if (conn->errorMessage.len == 0)