Skip to content

Commit 24178e2

Browse files
Refactor SASL exchange to return tri-state status
The SASL exchange callback returned state in to output variables: done and success. This refactors that logic by introducing a new return variable of type SASLStatus which makes the code easier to read and understand, and prepares for future SASL exchanges which operate asynchronously. This was extracted from a larger patchset to introduce OAuthBearer authentication and authorization. Author: Jacob Champion <jacob.champion@enterprisedb.com> Discussion: https://postgr.es/m/d1b467a78e0e36ed85a09adf979d04cf124a9d4b.camel@vmware.com
1 parent 1db6897 commit 24178e2

File tree

4 files changed

+71
-67
lines changed

4 files changed

+71
-67
lines changed

src/interfaces/libpq/fe-auth-sasl.h

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@
2121

2222
#include "libpq-fe.h"
2323

24+
/*
25+
* Possible states for the SASL exchange, see the comment on exchange for an
26+
* explanation of these.
27+
*/
28+
typedef enum
29+
{
30+
SASL_COMPLETE = 0,
31+
SASL_FAILED,
32+
SASL_CONTINUE,
33+
} SASLStatus;
34+
2435
/*
2536
* Frontend SASL mechanism callbacks.
2637
*
@@ -59,7 +70,8 @@ typedef struct pg_fe_sasl_mech
5970
* Produces a client response to a server challenge. As a special case
6071
* for client-first SASL mechanisms, exchange() is called with a NULL
6172
* server response once at the start of the authentication exchange to
62-
* generate an initial response.
73+
* generate an initial response. Returns a SASLStatus indicating the
74+
* state and status of the exchange.
6375
*
6476
* Input parameters:
6577
*
@@ -79,22 +91,23 @@ typedef struct pg_fe_sasl_mech
7991
*
8092
* output: A malloc'd buffer containing the client's response to
8193
* the server (can be empty), or NULL if the exchange should
82-
* be aborted. (*success should be set to false in the
94+
* be aborted. (The callback should return SASL_FAILED in the
8395
* latter case.)
8496
*
8597
* outputlen: The length (0 or higher) of the client response buffer,
8698
* ignored if output is NULL.
8799
*
88-
* done: Set to true if the SASL exchange should not continue,
89-
* because the exchange is either complete or failed
100+
* Return value:
90101
*
91-
* success: Set to true if the SASL exchange completed successfully.
92-
* Ignored if *done is false.
102+
* SASL_CONTINUE: The output buffer is filled with a client response.
103+
* Additional server challenge is expected
104+
* SASL_COMPLETE: The SASL exchange has completed successfully.
105+
* SASL_FAILED: The exchange has failed and the connection should be
106+
* dropped.
93107
*--------
94108
*/
95-
void (*exchange) (void *state, char *input, int inputlen,
96-
char **output, int *outputlen,
97-
bool *done, bool *success);
109+
SASLStatus (*exchange) (void *state, char *input, int inputlen,
110+
char **output, int *outputlen);
98111

99112
/*--------
100113
* channel_bound()

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

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@
2424
/* The exported SCRAM callback mechanism. */
2525
static void *scram_init(PGconn *conn, const char *password,
2626
const char *sasl_mechanism);
27-
static void scram_exchange(void *opaq, char *input, int inputlen,
28-
char **output, int *outputlen,
29-
bool *done, bool *success);
27+
static SASLStatus scram_exchange(void *opaq, char *input, int inputlen,
28+
char **output, int *outputlen);
3029
static bool scram_channel_bound(void *opaq);
3130
static void scram_free(void *opaq);
3231

@@ -202,17 +201,14 @@ scram_free(void *opaq)
202201
/*
203202
* Exchange a SCRAM message with backend.
204203
*/
205-
static void
204+
static SASLStatus
206205
scram_exchange(void *opaq, char *input, int inputlen,
207-
char **output, int *outputlen,
208-
bool *done, bool *success)
206+
char **output, int *outputlen)
209207
{
210208
fe_scram_state *state = (fe_scram_state *) opaq;
211209
PGconn *conn = state->conn;
212210
const char *errstr = NULL;
213211

214-
*done = false;
215-
*success = false;
216212
*output = NULL;
217213
*outputlen = 0;
218214

@@ -225,12 +221,12 @@ scram_exchange(void *opaq, char *input, int inputlen,
225221
if (inputlen == 0)
226222
{
227223
libpq_append_conn_error(conn, "malformed SCRAM message (empty message)");
228-
goto error;
224+
return SASL_FAILED;
229225
}
230226
if (inputlen != strlen(input))
231227
{
232228
libpq_append_conn_error(conn, "malformed SCRAM message (length mismatch)");
233-
goto error;
229+
return SASL_FAILED;
234230
}
235231
}
236232

@@ -240,61 +236,59 @@ scram_exchange(void *opaq, char *input, int inputlen,
240236
/* Begin the SCRAM handshake, by sending client nonce */
241237
*output = build_client_first_message(state);
242238
if (*output == NULL)
243-
goto error;
239+
return SASL_FAILED;
244240

245241
*outputlen = strlen(*output);
246-
*done = false;
247242
state->state = FE_SCRAM_NONCE_SENT;
248-
break;
243+
return SASL_CONTINUE;
249244

250245
case FE_SCRAM_NONCE_SENT:
251246
/* Receive salt and server nonce, send response. */
252247
if (!read_server_first_message(state, input))
253-
goto error;
248+
return SASL_FAILED;
254249

255250
*output = build_client_final_message(state);
256251
if (*output == NULL)
257-
goto error;
252+
return SASL_FAILED;
258253

259254
*outputlen = strlen(*output);
260-
*done = false;
261255
state->state = FE_SCRAM_PROOF_SENT;
262-
break;
256+
return SASL_CONTINUE;
263257

264258
case FE_SCRAM_PROOF_SENT:
265-
/* Receive server signature */
266-
if (!read_server_final_message(state, input))
267-
goto error;
268-
269-
/*
270-
* Verify server signature, to make sure we're talking to the
271-
* genuine server.
272-
*/
273-
if (!verify_server_signature(state, success, &errstr))
274-
{
275-
libpq_append_conn_error(conn, "could not verify server signature: %s", errstr);
276-
goto error;
277-
}
278-
279-
if (!*success)
280259
{
281-
libpq_append_conn_error(conn, "incorrect server signature");
260+
bool match;
261+
262+
/* Receive server signature */
263+
if (!read_server_final_message(state, input))
264+
return SASL_FAILED;
265+
266+
/*
267+
* Verify server signature, to make sure we're talking to the
268+
* genuine server.
269+
*/
270+
if (!verify_server_signature(state, &match, &errstr))
271+
{
272+
libpq_append_conn_error(conn, "could not verify server signature: %s", errstr);
273+
return SASL_FAILED;
274+
}
275+
276+
if (!match)
277+
{
278+
libpq_append_conn_error(conn, "incorrect server signature");
279+
}
280+
state->state = FE_SCRAM_FINISHED;
281+
state->conn->client_finished_auth = true;
282+
return match ? SASL_COMPLETE : SASL_FAILED;
282283
}
283-
*done = true;
284-
state->state = FE_SCRAM_FINISHED;
285-
state->conn->client_finished_auth = true;
286-
break;
287284

288285
default:
289286
/* shouldn't happen */
290287
libpq_append_conn_error(conn, "invalid SCRAM exchange state");
291-
goto error;
288+
break;
292289
}
293-
return;
294290

295-
error:
296-
*done = true;
297-
*success = false;
291+
return SASL_FAILED;
298292
}
299293

300294
/*

src/interfaces/libpq/fe-auth.c

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -423,11 +423,10 @@ pg_SASL_init(PGconn *conn, int payloadlen)
423423
{
424424
char *initialresponse = NULL;
425425
int initialresponselen;
426-
bool done;
427-
bool success;
428426
const char *selected_mechanism;
429427
PQExpBufferData mechanism_buf;
430428
char *password;
429+
SASLStatus status;
431430

432431
initPQExpBuffer(&mechanism_buf);
433432

@@ -575,12 +574,11 @@ pg_SASL_init(PGconn *conn, int payloadlen)
575574
goto oom_error;
576575

577576
/* Get the mechanism-specific Initial Client Response, if any */
578-
conn->sasl->exchange(conn->sasl_state,
579-
NULL, -1,
580-
&initialresponse, &initialresponselen,
581-
&done, &success);
577+
status = conn->sasl->exchange(conn->sasl_state,
578+
NULL, -1,
579+
&initialresponse, &initialresponselen);
582580

583-
if (done && !success)
581+
if (status == SASL_FAILED)
584582
goto error;
585583

586584
/*
@@ -629,10 +627,9 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
629627
{
630628
char *output;
631629
int outputlen;
632-
bool done;
633-
bool success;
634630
int res;
635631
char *challenge;
632+
SASLStatus status;
636633

637634
/* Read the SASL challenge from the AuthenticationSASLContinue message. */
638635
challenge = malloc(payloadlen + 1);
@@ -651,13 +648,12 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
651648
/* For safety and convenience, ensure the buffer is NULL-terminated. */
652649
challenge[payloadlen] = '\0';
653650

654-
conn->sasl->exchange(conn->sasl_state,
655-
challenge, payloadlen,
656-
&output, &outputlen,
657-
&done, &success);
651+
status = conn->sasl->exchange(conn->sasl_state,
652+
challenge, payloadlen,
653+
&output, &outputlen);
658654
free(challenge); /* don't need the input anymore */
659655

660-
if (final && !done)
656+
if (final && status == SASL_CONTINUE)
661657
{
662658
if (outputlen != 0)
663659
free(output);
@@ -670,7 +666,7 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
670666
* If the exchange is not completed yet, we need to make sure that the
671667
* SASL mechanism has generated a message to send back.
672668
*/
673-
if (output == NULL && !done)
669+
if (output == NULL && status == SASL_CONTINUE)
674670
{
675671
libpq_append_conn_error(conn, "no client response found after SASL exchange success");
676672
return STATUS_ERROR;
@@ -692,7 +688,7 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
692688
return STATUS_ERROR;
693689
}
694690

695-
if (done && !success)
691+
if (status == SASL_FAILED)
696692
return STATUS_ERROR;
697693

698694
return STATUS_OK;

src/tools/pgindent/typedefs.list

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2442,6 +2442,7 @@ RuleLock
24422442
RuleStmt
24432443
RunningTransactions
24442444
RunningTransactionsData
2445+
SASLStatus
24452446
SC_HANDLE
24462447
SECURITY_ATTRIBUTES
24472448
SECURITY_STATUS

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy