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 <[email protected]> Discussion: https://postgr.es/m/[email protected]
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

+22-9
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

+36-42
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

+12-16
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

+1
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)