aboutsummaryrefslogtreecommitdiff
path: root/src/backend/libpq/auth.c
diff options
context:
space:
mode:
authorMichael Paquier <michael@paquier.xyz>2021-07-07 10:55:15 +0900
committerMichael Paquier <michael@paquier.xyz>2021-07-07 10:55:15 +0900
commit9fd85570d179f10f93344d722005f7086b3c31ca (patch)
treea678636aee49619b4e69f45a67df6f9498d59104 /src/backend/libpq/auth.c
parent955b3e0f9269639fb916cee3dea37aee50b82df0 (diff)
downloadpostgresql-9fd85570d179f10f93344d722005f7086b3c31ca.tar.gz
postgresql-9fd85570d179f10f93344d722005f7086b3c31ca.zip
Refactor SASL code with a generic interface for its mechanisms
The code of SCRAM and SASL have been tightly linked together since SCRAM exists in the core code, making hard to apprehend the addition of new SASL mechanisms, but these are by design different facilities, with SCRAM being an option for SASL. This refactors the code related to both so as the backend and the frontend use a set of callbacks for SASL mechanisms, documenting while on it what is expected by anybody adding a new SASL mechanism. The separation between both layers is neat, using two sets of callbacks for the frontend and the backend to mark the frontier between both facilities. The shape of the callbacks is now directly inspired from the routines used by SCRAM, so the code change is straight-forward, and the SASL code is moved into its own set of files. These will likely change depending on how and if new SASL mechanisms get added in the future. Author: Jacob Champion Reviewed-by: Michael Paquier Discussion: https://postgr.es/m/3d2a6f5d50e741117d6baf83eb67ebf1a8a35a11.camel@vmware.com
Diffstat (limited to 'src/backend/libpq/auth.c')
-rw-r--r--src/backend/libpq/auth.c167
1 files changed, 6 insertions, 161 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 967b5ef73cc..8cc23ef7fb4 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -26,11 +26,11 @@
#include "commands/user.h"
#include "common/ip.h"
#include "common/md5.h"
-#include "common/scram-common.h"
#include "libpq/auth.h"
#include "libpq/crypt.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
+#include "libpq/sasl.h"
#include "libpq/scram.h"
#include "miscadmin.h"
#include "port/pg_bswap.h"
@@ -45,8 +45,6 @@
* Global authentication functions
*----------------------------------------------------------------
*/
-static void sendAuthRequest(Port *port, AuthRequest areq, const char *extradata,
- int extralen);
static void auth_failed(Port *port, int status, char *logdetail);
static char *recv_password_packet(Port *port);
static void set_authn_id(Port *port, const char *id);
@@ -60,7 +58,6 @@ static int CheckPasswordAuth(Port *port, char **logdetail);
static int CheckPWChallengeAuth(Port *port, char **logdetail);
static int CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail);
-static int CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail);
/*----------------------------------------------------------------
@@ -224,14 +221,6 @@ static int PerformRadiusTransaction(const char *server, const char *secret, cons
*/
#define PG_MAX_AUTH_TOKEN_LENGTH 65535
-/*
- * Maximum accepted size of SASL messages.
- *
- * The messages that the server or libpq generate are much smaller than this,
- * but have some headroom.
- */
-#define PG_MAX_SASL_MESSAGE_LENGTH 1024
-
/*----------------------------------------------------------------
* Global authentication functions
*----------------------------------------------------------------
@@ -668,7 +657,7 @@ ClientAuthentication(Port *port)
/*
* Send an authentication request packet to the frontend.
*/
-static void
+void
sendAuthRequest(Port *port, AuthRequest areq, const char *extradata, int extralen)
{
StringInfoData buf;
@@ -848,12 +837,14 @@ CheckPWChallengeAuth(Port *port, char **logdetail)
* SCRAM secret, we must do SCRAM authentication.
*
* If MD5 authentication is not allowed, always use SCRAM. If the user
- * had an MD5 password, CheckSCRAMAuth() will fail.
+ * had an MD5 password, CheckSASLAuth() with the SCRAM mechanism will
+ * fail.
*/
if (port->hba->auth_method == uaMD5 && pwtype == PASSWORD_TYPE_MD5)
auth_result = CheckMD5Auth(port, shadow_pass, logdetail);
else
- auth_result = CheckSCRAMAuth(port, shadow_pass, logdetail);
+ auth_result = CheckSASLAuth(&pg_be_scram_mech, port, shadow_pass,
+ logdetail);
if (shadow_pass)
pfree(shadow_pass);
@@ -911,152 +902,6 @@ CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail)
return result;
}
-static int
-CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
-{
- StringInfoData sasl_mechs;
- int mtype;
- StringInfoData buf;
- void *scram_opaq = NULL;
- char *output = NULL;
- int outputlen = 0;
- const char *input;
- int inputlen;
- int result;
- bool initial;
-
- /*
- * Send the SASL authentication request to user. It includes the list of
- * authentication mechanisms that are supported.
- */
- initStringInfo(&sasl_mechs);
-
- pg_be_scram_get_mechanisms(port, &sasl_mechs);
- /* Put another '\0' to mark that list is finished. */
- appendStringInfoChar(&sasl_mechs, '\0');
-
- sendAuthRequest(port, AUTH_REQ_SASL, sasl_mechs.data, sasl_mechs.len);
- pfree(sasl_mechs.data);
-
- /*
- * Loop through SASL message exchange. This exchange can consist of
- * multiple messages sent in both directions. First message is always
- * from the client. All messages from client to server are password
- * packets (type 'p').
- */
- initial = true;
- do
- {
- pq_startmsgread();
- mtype = pq_getbyte();
- if (mtype != 'p')
- {
- /* Only log error if client didn't disconnect. */
- if (mtype != EOF)
- {
- ereport(ERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("expected SASL response, got message type %d",
- mtype)));
- }
- else
- return STATUS_EOF;
- }
-
- /* Get the actual SASL message */
- initStringInfo(&buf);
- if (pq_getmessage(&buf, PG_MAX_SASL_MESSAGE_LENGTH))
- {
- /* EOF - pq_getmessage already logged error */
- pfree(buf.data);
- return STATUS_ERROR;
- }
-
- elog(DEBUG4, "processing received SASL response of length %d", buf.len);
-
- /*
- * The first SASLInitialResponse message is different from the others.
- * It indicates which SASL mechanism the client selected, and contains
- * an optional Initial Client Response payload. The subsequent
- * SASLResponse messages contain just the SASL payload.
- */
- if (initial)
- {
- const char *selected_mech;
-
- selected_mech = pq_getmsgrawstring(&buf);
-
- /*
- * Initialize the status tracker for message exchanges.
- *
- * If the user doesn't exist, or doesn't have a valid password, or
- * it's expired, we still go through the motions of SASL
- * authentication, but tell the authentication method that the
- * authentication is "doomed". That is, it's going to fail, no
- * matter what.
- *
- * This is because we don't want to reveal to an attacker what
- * usernames are valid, nor which users have a valid password.
- */
- scram_opaq = pg_be_scram_init(port, selected_mech, shadow_pass);
-
- inputlen = pq_getmsgint(&buf, 4);
- if (inputlen == -1)
- input = NULL;
- else
- input = pq_getmsgbytes(&buf, inputlen);
-
- initial = false;
- }
- else
- {
- inputlen = buf.len;
- input = pq_getmsgbytes(&buf, buf.len);
- }
- pq_getmsgend(&buf);
-
- /*
- * The StringInfo guarantees that there's a \0 byte after the
- * response.
- */
- Assert(input == NULL || input[inputlen] == '\0');
-
- /*
- * we pass 'logdetail' as NULL when doing a mock authentication,
- * because we should already have a better error message in that case
- */
- result = pg_be_scram_exchange(scram_opaq, input, inputlen,
- &output, &outputlen,
- logdetail);
-
- /* input buffer no longer used */
- pfree(buf.data);
-
- if (output)
- {
- /*
- * Negotiation generated data to be sent to the client.
- */
- elog(DEBUG4, "sending SASL challenge of length %u", outputlen);
-
- if (result == SASL_EXCHANGE_SUCCESS)
- sendAuthRequest(port, AUTH_REQ_SASL_FIN, output, outputlen);
- else
- sendAuthRequest(port, AUTH_REQ_SASL_CONT, output, outputlen);
-
- pfree(output);
- }
- } while (result == SASL_EXCHANGE_CONTINUE);
-
- /* Oops, Something bad happened */
- if (result != SASL_EXCHANGE_SUCCESS)
- {
- return STATUS_ERROR;
- }
-
- return STATUS_OK;
-}
-
/*----------------------------------------------------------------
* GSSAPI authentication system