Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita99a32e

Browse files
libpq: Handle asynchronous actions during SASL
This adds the ability for a SASL mechanism to signal PQconnectPoll()that some arbitrary work, external to the Postgres connection, isrequired for authentication to continue. There is no consumer forthis capability as part of this commit, it is infrastructure whichis required for future work on supporting the OAUTHBEARER mechanism.To ensure that threads are not blocked waiting for the SASL mechanismto make long-running calls, the mechanism communicates with the top-level client via the "altsock": a file or socket descriptor, opaque tothis layer of libpq, which is signaled when work is ready to be doneagain. The altsock temporarily replaces the regular connectiondescriptor, so existing PQsocket() clients should continue to operatecorrectly using their existing polling implementations.For a mechanism to use this it should set an authentication callback,conn->async_auth(), and a cleanup callback, conn->cleanup_async_auth(),and return SASL_ASYNC during the exchange. It should then assignconn->altsock during the first call to async_auth(). When the cleanupcallback is called, either because authentication has succeeded orbecause the connection is being dropped, the altsock must be releasedand disconnected from the PGconn object.This was extracted from the larger OAUTHBEARER patchset which hasbeen developed, and reviewed by many, over several years and it isthus likely that some reviewer credit of much earlier versions hasbeen accidentally omitted.Author: Jacob Champion <jacob.champion@enterprisedb.com>Reviewed-by: Daniel Gustafsson <daniel@yesql.se>Reviewed-by: Peter Eisentraut <peter@eisentraut.org>Reviewed-by: Antonin Houska <ah@cybertec.at>Discussion:https://postgr.es/m/CAOYmi+kJqzo6XsR9TEhvVfeVNQ-TyFM5LATypm9yoQVYk=4Wrw@mail.gmail.com
1 parent44ec095 commita99a32e

File tree

8 files changed

+227
-49
lines changed

8 files changed

+227
-49
lines changed

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ typedef enum
3030
SASL_COMPLETE=0,
3131
SASL_FAILED,
3232
SASL_CONTINUE,
33+
SASL_ASYNC,
3334
}SASLStatus;
3435

3536
/*
@@ -77,6 +78,8 @@ typedef struct pg_fe_sasl_mech
7778
*
7879
*state: The opaque mechanism state returned by init()
7980
*
81+
*final: true if the server has sent a final exchange outcome
82+
*
8083
*input: The challenge data sent by the server, or NULL when
8184
* generating a client-first initial response (that is, when
8285
* the server expects the client to send a message to start
@@ -101,12 +104,18 @@ typedef struct pg_fe_sasl_mech
101104
*
102105
*SASL_CONTINUE:The output buffer is filled with a client response.
103106
*Additional server challenge is expected
107+
*SASL_ASYNC:Some asynchronous processing external to the
108+
*connection needs to be done before a response can be
109+
*generated. The mechanism is responsible for setting up
110+
*conn->async_auth/cleanup_async_auth appropriately
111+
*before returning.
104112
*SASL_COMPLETE:The SASL exchange has completed successfully.
105113
*SASL_FAILED:The exchange has failed and the connection should be
106114
*dropped.
107115
*--------
108116
*/
109-
SASLStatus(*exchange) (void*state,char*input,intinputlen,
117+
SASLStatus(*exchange) (void*state,boolfinal,
118+
char*input,intinputlen,
110119
char**output,int*outputlen);
111120

112121
/*--------

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
/* The exported SCRAM callback mechanism. */
2525
staticvoid*scram_init(PGconn*conn,constchar*password,
2626
constchar*sasl_mechanism);
27-
staticSASLStatusscram_exchange(void*opaq,char*input,intinputlen,
27+
staticSASLStatusscram_exchange(void*opaq,boolfinal,
28+
char*input,intinputlen,
2829
char**output,int*outputlen);
2930
staticboolscram_channel_bound(void*opaq);
3031
staticvoidscram_free(void*opaq);
@@ -205,7 +206,8 @@ scram_free(void *opaq)
205206
* Exchange a SCRAM message with backend.
206207
*/
207208
staticSASLStatus
208-
scram_exchange(void*opaq,char*input,intinputlen,
209+
scram_exchange(void*opaq,boolfinal,
210+
char*input,intinputlen,
209211
char**output,int*outputlen)
210212
{
211213
fe_scram_state*state= (fe_scram_state*)opaq;

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

Lines changed: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)
430430
* Initialize SASL authentication exchange.
431431
*/
432432
staticint
433-
pg_SASL_init(PGconn*conn,intpayloadlen)
433+
pg_SASL_init(PGconn*conn,intpayloadlen,bool*async)
434434
{
435435
char*initialresponse=NULL;
436436
intinitialresponselen;
@@ -448,7 +448,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
448448
gotoerror;
449449
}
450450

451-
if (conn->sasl_state)
451+
if (conn->sasl_state&& !conn->async_auth)
452452
{
453453
libpq_append_conn_error(conn,"duplicate SASL authentication request");
454454
gotoerror;
@@ -607,26 +607,54 @@ pg_SASL_init(PGconn *conn, int payloadlen)
607607

608608
Assert(conn->sasl);
609609

610-
/*
611-
* Initialize the SASL state information with all the information gathered
612-
* during the initial exchange.
613-
*
614-
* Note: Only tls-unique is supported for the moment.
615-
*/
616-
conn->sasl_state=conn->sasl->init(conn,
617-
password,
618-
selected_mechanism);
619610
if (!conn->sasl_state)
620-
gotooom_error;
611+
{
612+
/*
613+
* Initialize the SASL state information with all the information
614+
* gathered during the initial exchange.
615+
*
616+
* Note: Only tls-unique is supported for the moment.
617+
*/
618+
conn->sasl_state=conn->sasl->init(conn,
619+
password,
620+
selected_mechanism);
621+
if (!conn->sasl_state)
622+
gotooom_error;
623+
}
624+
else
625+
{
626+
/*
627+
* This is only possible if we're returning from an async loop.
628+
* Disconnect it now.
629+
*/
630+
Assert(conn->async_auth);
631+
conn->async_auth=NULL;
632+
}
621633

622634
/* Get the mechanism-specific Initial Client Response, if any */
623-
status=conn->sasl->exchange(conn->sasl_state,
635+
status=conn->sasl->exchange(conn->sasl_state, false,
624636
NULL,-1,
625637
&initialresponse,&initialresponselen);
626638

627639
if (status==SASL_FAILED)
628640
gotoerror;
629641

642+
if (status==SASL_ASYNC)
643+
{
644+
/*
645+
* The mechanism should have set up the necessary callbacks; all we
646+
* need to do is signal the caller.
647+
*
648+
* In non-assertion builds, this postcondition is enforced at time of
649+
* use in PQconnectPoll().
650+
*/
651+
Assert(conn->async_auth);
652+
Assert(conn->cleanup_async_auth);
653+
654+
*async= true;
655+
returnSTATUS_OK;
656+
}
657+
630658
/*
631659
* Build a SASLInitialResponse message, and send it.
632660
*/
@@ -671,7 +699,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
671699
* the protocol.
672700
*/
673701
staticint
674-
pg_SASL_continue(PGconn*conn,intpayloadlen,boolfinal)
702+
pg_SASL_continue(PGconn*conn,intpayloadlen,boolfinal,bool*async)
675703
{
676704
char*output;
677705
intoutputlen;
@@ -701,11 +729,25 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
701729
/* For safety and convenience, ensure the buffer is NULL-terminated. */
702730
challenge[payloadlen]='\0';
703731

704-
status=conn->sasl->exchange(conn->sasl_state,
732+
status=conn->sasl->exchange(conn->sasl_state,final,
705733
challenge,payloadlen,
706734
&output,&outputlen);
707735
free(challenge);/* don't need the input anymore */
708736

737+
if (status==SASL_ASYNC)
738+
{
739+
/*
740+
* The mechanism should have set up the necessary callbacks; all we
741+
* need to do is signal the caller.
742+
*/
743+
*async= true;
744+
745+
/*
746+
* The mechanism may optionally generate some output to send before
747+
* switching over to async auth, so continue onwards.
748+
*/
749+
}
750+
709751
if (final&&status==SASL_CONTINUE)
710752
{
711753
if (outputlen!=0)
@@ -1013,12 +1055,18 @@ check_expected_areq(AuthRequest areq, PGconn *conn)
10131055
* it. We are responsible for reading any remaining extra data, specific
10141056
* to the authentication method. 'payloadlen' is the remaining length in
10151057
* the message.
1058+
*
1059+
* If *async is set to true on return, the client doesn't yet have enough
1060+
* information to respond, and the caller must temporarily switch to
1061+
* conn->async_auth() to continue driving the exchange.
10161062
*/
10171063
int
1018-
pg_fe_sendauth(AuthRequestareq,intpayloadlen,PGconn*conn)
1064+
pg_fe_sendauth(AuthRequestareq,intpayloadlen,PGconn*conn,bool*async)
10191065
{
10201066
intoldmsglen;
10211067

1068+
*async= false;
1069+
10221070
if (!check_expected_areq(areq,conn))
10231071
returnSTATUS_ERROR;
10241072

@@ -1176,7 +1224,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
11761224
* The request contains the name (as assigned by IANA) of the
11771225
* authentication mechanism.
11781226
*/
1179-
if (pg_SASL_init(conn,payloadlen)!=STATUS_OK)
1227+
if (pg_SASL_init(conn,payloadlen,async)!=STATUS_OK)
11801228
{
11811229
/* pg_SASL_init already set the error message */
11821230
returnSTATUS_ERROR;
@@ -1185,23 +1233,33 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
11851233

11861234
caseAUTH_REQ_SASL_CONT:
11871235
caseAUTH_REQ_SASL_FIN:
1188-
if (conn->sasl_state==NULL)
11891236
{
1190-
appendPQExpBufferStr(&conn->errorMessage,
1191-
"fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without AUTH_REQ_SASL\n");
1192-
returnSTATUS_ERROR;
1193-
}
1194-
oldmsglen=conn->errorMessage.len;
1195-
if (pg_SASL_continue(conn,payloadlen,
1196-
(areq==AUTH_REQ_SASL_FIN))!=STATUS_OK)
1197-
{
1198-
/* Use this message if pg_SASL_continue didn't supply one */
1199-
if (conn->errorMessage.len==oldmsglen)
1237+
boolfinal= false;
1238+
1239+
if (conn->sasl_state==NULL)
1240+
{
12001241
appendPQExpBufferStr(&conn->errorMessage,
1201-
"fe_sendauth: error in SASL authentication\n");
1202-
returnSTATUS_ERROR;
1242+
"fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without AUTH_REQ_SASL\n");
1243+
returnSTATUS_ERROR;
1244+
}
1245+
oldmsglen=conn->errorMessage.len;
1246+
1247+
if (areq==AUTH_REQ_SASL_FIN)
1248+
final= true;
1249+
1250+
if (pg_SASL_continue(conn,payloadlen,final,async)!=STATUS_OK)
1251+
{
1252+
/*
1253+
* Append a generic error message unless pg_SASL_continue
1254+
* did set a more specific one already.
1255+
*/
1256+
if (conn->errorMessage.len==oldmsglen)
1257+
appendPQExpBufferStr(&conn->errorMessage,
1258+
"fe_sendauth: error in SASL authentication\n");
1259+
returnSTATUS_ERROR;
1260+
}
1261+
break;
12031262
}
1204-
break;
12051263

12061264
default:
12071265
libpq_append_conn_error(conn,"authentication method %u not supported",areq);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020

2121
/* Prototypes for functions in fe-auth.c */
22-
externintpg_fe_sendauth(AuthRequestareq,intpayloadlen,PGconn*conn);
22+
externintpg_fe_sendauth(AuthRequestareq,intpayloadlen,PGconn*conn,
23+
bool*async);
2324
externchar*pg_fe_getusername(uid_tuser_id,PQExpBuffererrorMessage);
2425
externchar*pg_fe_getauthname(PQExpBuffererrorMessage);
2526

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp