Skip to content

Commit 6647248

Browse files
committed
Don't allow immediate interrupts during authentication anymore.
We used to handle authentication_timeout by setting ImmediateInterruptOK to true during large parts of the authentication phase of a new connection. While that happens to work acceptably in practice, it's not particularly nice and has ugly corner cases. Previous commits converted the FE/BE communication to use latches and implemented support for interrupt handling during both send/recv. Building on top of that work we can get rid of ImmediateInterruptOK during authentication, by immediately treating timeouts during authentication as a reason to die. As die interrupts are handled immediately during client communication that provides a sensibly quick reaction time to authentication timeout. Additionally add a few CHECK_FOR_INTERRUPTS() to some more complex authentication methods. More could be added, but this already should provides a reasonable coverage. While it this overall increases the maximum time till a timeout is reacted to, it greatly reduces complexity and increases reliability. That seems like a overall win. If the increase proves to be noticeable we can deal with those cases by moving to nonblocking network code and add interrupt checking there. Reviewed-By: Heikki Linnakangas
1 parent cec916f commit 6647248

File tree

5 files changed

+41
-37
lines changed

5 files changed

+41
-37
lines changed

src/backend/libpq/auth.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -306,13 +306,6 @@ ClientAuthentication(Port *port)
306306
*/
307307
hba_getauthmethod(port);
308308

309-
/*
310-
* Enable immediate response to SIGTERM/SIGINT/timeout interrupts. (We
311-
* don't want this during hba_getauthmethod() because it might have to do
312-
* database access, eg for role membership checks.)
313-
*/
314-
ImmediateInterruptOK = true;
315-
/* And don't forget to detect one that already arrived */
316309
CHECK_FOR_INTERRUPTS();
317310

318311
/*
@@ -566,9 +559,6 @@ ClientAuthentication(Port *port)
566559
sendAuthRequest(port, AUTH_REQ_OK);
567560
else
568561
auth_failed(port, status, logdetail);
569-
570-
/* Done with authentication, so we should turn off immediate interrupts */
571-
ImmediateInterruptOK = false;
572562
}
573563

574564

@@ -580,6 +570,8 @@ sendAuthRequest(Port *port, AuthRequest areq)
580570
{
581571
StringInfoData buf;
582572

573+
CHECK_FOR_INTERRUPTS();
574+
583575
pq_beginmessage(&buf, 'R');
584576
pq_sendint(&buf, (int32) areq, sizeof(int32));
585577

@@ -613,6 +605,8 @@ sendAuthRequest(Port *port, AuthRequest areq)
613605
*/
614606
if (areq != AUTH_REQ_OK)
615607
pq_flush();
608+
609+
CHECK_FOR_INTERRUPTS();
616610
}
617611

618612
/*
@@ -851,6 +845,9 @@ pg_GSS_recvauth(Port *port)
851845
do
852846
{
853847
pq_startmsgread();
848+
849+
CHECK_FOR_INTERRUPTS();
850+
854851
mtype = pq_getbyte();
855852
if (mtype != 'p')
856853
{
@@ -900,6 +897,8 @@ pg_GSS_recvauth(Port *port)
900897
maj_stat, min_stat,
901898
(unsigned int) port->gss->outbuf.length, gflags);
902899

900+
CHECK_FOR_INTERRUPTS();
901+
903902
if (port->gss->outbuf.length != 0)
904903
{
905904
/*
@@ -1396,6 +1395,9 @@ interpret_ident_response(const char *ident_response,
13961395
* IP addresses and port numbers are in network byte order.
13971396
*
13981397
* But iff we're unable to get the information from ident, return false.
1398+
*
1399+
* XXX: Using WaitLatchOrSocket() and doing a CHECK_FOR_INTERRUPTS() if the
1400+
* latch was set would improve the responsiveness to timeouts/cancellations.
13991401
*/
14001402
static int
14011403
ident_inet(hbaPort *port)
@@ -1510,6 +1512,8 @@ ident_inet(hbaPort *port)
15101512
/* loop in case send is interrupted */
15111513
do
15121514
{
1515+
CHECK_FOR_INTERRUPTS();
1516+
15131517
rc = send(sock_fd, ident_query, strlen(ident_query), 0);
15141518
} while (rc < 0 && errno == EINTR);
15151519

@@ -1525,6 +1529,8 @@ ident_inet(hbaPort *port)
15251529

15261530
do
15271531
{
1532+
CHECK_FOR_INTERRUPTS();
1533+
15281534
rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
15291535
} while (rc < 0 && errno == EINTR);
15301536

@@ -2413,6 +2419,10 @@ CheckRADIUSAuth(Port *port)
24132419
* call to select() with a timeout, since somebody can be sending invalid
24142420
* packets to our port thus causing us to retry in a loop and never time
24152421
* out.
2422+
*
2423+
* XXX: Using WaitLatchOrSocket() and doing a CHECK_FOR_INTERRUPTS() if
2424+
* the latch was set would improve the responsiveness to
2425+
* timeouts/cancellations.
24162426
*/
24172427
gettimeofday(&endtime, NULL);
24182428
endtime.tv_sec += RADIUS_TIMEOUT;

src/backend/libpq/be-secure-openssl.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,11 @@ be_tls_open_server(Port *port)
377377
/* not allowed during connection establishment */
378378
Assert(!port->noblock);
379379

380+
/*
381+
* No need to care about timeouts/interrupts here. At this
382+
* point authentication_timeout still employs
383+
* StartupPacketTimeoutHandler() which directly exits.
384+
*/
380385
if (err == SSL_ERROR_WANT_READ)
381386
waitfor = WL_SOCKET_READABLE;
382387
else

src/backend/libpq/crypt.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,6 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
4747
Datum datum;
4848
bool isnull;
4949

50-
/*
51-
* Disable immediate interrupts while doing database access. (Note we
52-
* don't bother to turn this back on if we hit one of the failure
53-
* conditions, since we can expect we'll just exit right away anyway.)
54-
*/
55-
ImmediateInterruptOK = false;
56-
5750
/* Get role info from pg_authid */
5851
roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
5952
if (!HeapTupleIsValid(roleTup))
@@ -80,9 +73,6 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
8073
if (*shadow_pass == '\0')
8174
return STATUS_ERROR; /* empty password */
8275

83-
/* Re-enable immediate response to SIGTERM/SIGINT/timeout interrupts */
84-
ImmediateInterruptOK = true;
85-
/* And don't forget to detect one that already arrived */
8676
CHECK_FOR_INTERRUPTS();
8777

8878
/*

src/backend/tcop/postgres.c

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,7 +2880,11 @@ ProcessInterrupts(void)
28802880
/* As in quickdie, don't risk sending to client during auth */
28812881
if (ClientAuthInProgress && whereToSendOutput == DestRemote)
28822882
whereToSendOutput = DestNone;
2883-
if (IsAutoVacuumWorkerProcess())
2883+
if (ClientAuthInProgress)
2884+
ereport(FATAL,
2885+
(errcode(ERRCODE_QUERY_CANCELED),
2886+
errmsg("canceling authentication due to timeout")));
2887+
else if (IsAutoVacuumWorkerProcess())
28842888
ereport(FATAL,
28852889
(errcode(ERRCODE_ADMIN_SHUTDOWN),
28862890
errmsg("terminating autovacuum process due to administrator command")));
@@ -2959,17 +2963,6 @@ ProcessInterrupts(void)
29592963
}
29602964

29612965
QueryCancelPending = false;
2962-
if (ClientAuthInProgress)
2963-
{
2964-
ImmediateInterruptOK = false; /* not idle anymore */
2965-
LockErrorCleanup();
2966-
/* As in quickdie, don't risk sending to client during auth */
2967-
if (whereToSendOutput == DestRemote)
2968-
whereToSendOutput = DestNone;
2969-
ereport(ERROR,
2970-
(errcode(ERRCODE_QUERY_CANCELED),
2971-
errmsg("canceling authentication due to timeout")));
2972-
}
29732966

29742967
/*
29752968
* If LOCK_TIMEOUT and STATEMENT_TIMEOUT indicators are both set, we

src/backend/utils/init/postinit.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,18 +1099,24 @@ ShutdownPostgres(int code, Datum arg)
10991099
static void
11001100
StatementTimeoutHandler(void)
11011101
{
1102+
int sig = SIGINT;
1103+
1104+
/*
1105+
* During authentication the timeout is used to deal with
1106+
* authentication_timeout - we want to quit in response to such timeouts.
1107+
*/
1108+
if (ClientAuthInProgress)
1109+
sig = SIGTERM;
1110+
11021111
#ifdef HAVE_SETSID
11031112
/* try to signal whole process group */
1104-
kill(-MyProcPid, SIGINT);
1113+
kill(-MyProcPid, sig);
11051114
#endif
1106-
kill(MyProcPid, SIGINT);
1115+
kill(MyProcPid, sig);
11071116
}
11081117

11091118
/*
11101119
* LOCK_TIMEOUT handler: trigger a query-cancel interrupt.
1111-
*
1112-
* This is identical to StatementTimeoutHandler, but since it's so short,
1113-
* we might as well keep the two functions separate for clarity.
11141120
*/
11151121
static void
11161122
LockTimeoutHandler(void)

0 commit comments

Comments
 (0)