Commit 35b7601b authored by Tom Lane's avatar Tom Lane

Add an overall timeout on the client authentication cycle, so that

a hung client or lost connection can't indefinitely block a postmaster
child (not to mention the possibility of deliberate DoS attacks).
Timeout is controlled by new authentication_timeout GUC variable,
which I set to 60 seconds by default ... does that seem reasonable?
parent e3f5bc34
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.82 2001/09/21 03:32:35 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.83 2001/09/21 17:06:12 tgl Exp $
--> -->
<Chapter Id="runtime"> <Chapter Id="runtime">
...@@ -1018,6 +1018,20 @@ env PGOPTIONS='-c geqo=off' psql ...@@ -1018,6 +1018,20 @@ env PGOPTIONS='-c geqo=off' psql
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>AUTHENTICATION_TIMEOUT</varname> (<type>integer</type>)</term>
<listitem>
<para>
Maximum time to complete client authentication, in seconds.
If a would-be client has not completed the authentication protocol
in this much time, the server unceremoniously breaks the connection.
This prevents hung clients from occupying a connection indefinitely.
This option can only be set at server start or in the
<filename>postgresql.conf</filename> file.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<indexterm> <indexterm>
<primary>deadlock</primary> <primary>deadlock</primary>
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/pqsignal.c,v 1.23 2001/09/08 01:10:20 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/libpq/pqsignal.c,v 1.24 2001/09/21 17:06:12 tgl Exp $
* *
* NOTES * NOTES
* This shouldn't be in libpq, but the monitor and some other * This shouldn't be in libpq, but the monitor and some other
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
* signals that should never be turned off. * signals that should never be turned off.
* *
* AuthBlockSig is the set of signals to block during authentication; * AuthBlockSig is the set of signals to block during authentication;
* it's essentially BlockSig minus SIGTERM and SIGQUIT. * it's essentially BlockSig minus SIGTERM, SIGQUIT, SIGALRM.
* *
* UnBlockSig is the set of signals to block when we don't want to block * UnBlockSig is the set of signals to block when we don't want to block
* signals (is this ever nonzero??) * signals (is this ever nonzero??)
...@@ -109,6 +109,9 @@ pqinitmask(void) ...@@ -109,6 +109,9 @@ pqinitmask(void)
#ifdef SIGQUIT #ifdef SIGQUIT
sigdelset(&AuthBlockSig, SIGQUIT); sigdelset(&AuthBlockSig, SIGQUIT);
#endif #endif
#ifdef SIGALRM
sigdelset(&AuthBlockSig, SIGALRM);
#endif
#else #else
UnBlockSig = 0; UnBlockSig = 0;
BlockSig = sigmask(SIGHUP) | sigmask(SIGQUIT) | BlockSig = sigmask(SIGHUP) | sigmask(SIGQUIT) |
...@@ -116,7 +119,7 @@ pqinitmask(void) ...@@ -116,7 +119,7 @@ pqinitmask(void)
sigmask(SIGINT) | sigmask(SIGUSR1) | sigmask(SIGINT) | sigmask(SIGUSR1) |
sigmask(SIGUSR2) | sigmask(SIGCHLD) | sigmask(SIGUSR2) | sigmask(SIGCHLD) |
sigmask(SIGWINCH) | sigmask(SIGFPE); sigmask(SIGWINCH) | sigmask(SIGFPE);
AuthBlockSig = sigmask(SIGHUP) | sigmask(SIGALRM) | AuthBlockSig = sigmask(SIGHUP) |
sigmask(SIGINT) | sigmask(SIGUSR1) | sigmask(SIGINT) | sigmask(SIGUSR1) |
sigmask(SIGUSR2) | sigmask(SIGCHLD) | sigmask(SIGUSR2) | sigmask(SIGCHLD) |
sigmask(SIGWINCH) | sigmask(SIGFPE); sigmask(SIGWINCH) | sigmask(SIGFPE);
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.241 2001/09/08 01:10:20 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.242 2001/09/21 17:06:12 tgl Exp $
* *
* NOTES * NOTES
* *
...@@ -190,6 +190,8 @@ bool NetServer = false; /* listen on TCP/IP */ ...@@ -190,6 +190,8 @@ bool NetServer = false; /* listen on TCP/IP */
bool EnableSSL = false; bool EnableSSL = false;
bool SilentMode = false; /* silent mode (-S) */ bool SilentMode = false; /* silent mode (-S) */
int PreAuthDelay = 0;
int AuthenticationTimeout = 60;
int CheckPointTimeout = 300; int CheckPointTimeout = 300;
/* Startup/shutdown state */ /* Startup/shutdown state */
...@@ -1941,16 +1943,38 @@ DoBackend(Port *port) ...@@ -1941,16 +1943,38 @@ DoBackend(Port *port)
/* Reset MyProcPid to new backend's pid */ /* Reset MyProcPid to new backend's pid */
MyProcPid = getpid(); MyProcPid = getpid();
/*
* Initialize libpq and enable reporting of elog errors to the client.
* Must do this now because authentication uses libpq to send messages.
*/
pq_init(); /* initialize libpq to talk to client */
whereToSendOutput = Remote; /* now safe to elog to client */
/* /*
* We arrange for a simple exit(0) if we receive SIGTERM or SIGQUIT * We arrange for a simple exit(0) if we receive SIGTERM or SIGQUIT
* during any client authentication related communication. Otherwise * during any client authentication related communication. Otherwise
* the postmaster cannot shutdown the database FAST or IMMED cleanly * the postmaster cannot shutdown the database FAST or IMMED cleanly
* if a buggy client blocks a backend during authentication. * if a buggy client blocks a backend during authentication. We also
* will exit(0) after a time delay, so that a broken client can't hog
* a connection indefinitely.
*
* PreAuthDelay is a debugging aid for investigating problems in the
* authentication cycle: it can be set in postgresql.conf to allow
* time to attach to the newly-forked backend with a debugger.
* (See also the -W backend switch, which we allow clients to pass
* through PGOPTIONS, but it is not honored until after authentication.)
*/ */
pqsignal(SIGTERM, authdie); pqsignal(SIGTERM, authdie);
pqsignal(SIGQUIT, authdie); pqsignal(SIGQUIT, authdie);
pqsignal(SIGALRM, authdie);
PG_SETMASK(&AuthBlockSig); PG_SETMASK(&AuthBlockSig);
if (PreAuthDelay > 0)
sleep(PreAuthDelay);
if (! enable_sigalrm_interrupt(AuthenticationTimeout * 1000))
elog(FATAL, "DoBackend: Unable to set timer for auth timeout");
/* /*
* Receive the startup packet (which might turn out to be a cancel * Receive the startup packet (which might turn out to be a cancel
* request packet); then perform client authentication. * request packet); then perform client authentication.
...@@ -1963,9 +1987,11 @@ DoBackend(Port *port) ...@@ -1963,9 +1987,11 @@ DoBackend(Port *port)
ClientAuthentication(MyProcPort); /* might not return, if failure */ ClientAuthentication(MyProcPort); /* might not return, if failure */
/* /*
* Done with authentication. Prevent SIGTERM/SIGQUIT again until * Done with authentication. Disable timeout, and prevent SIGTERM/SIGQUIT
* backend startup is complete. * again until backend startup is complete.
*/ */
if (! disable_sigalrm_interrupt())
elog(FATAL, "DoBackend: Unable to disable timer for auth timeout");
PG_SETMASK(&BlockSig); PG_SETMASK(&BlockSig);
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.107 2001/09/07 00:27:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.108 2001/09/21 17:06:12 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -327,18 +327,7 @@ LockWaitCancel(void) ...@@ -327,18 +327,7 @@ LockWaitCancel(void)
waitingForLock = false; waitingForLock = false;
/* Turn off the deadlock timer, if it's still running (see ProcSleep) */ /* Turn off the deadlock timer, if it's still running (see ProcSleep) */
#ifndef __BEOS__ disable_sigalrm_interrupt();
{
struct itimerval timeval,
dummy;
MemSet(&timeval, 0, sizeof(struct itimerval));
setitimer(ITIMER_REAL, &timeval, &dummy);
}
#else
/* BeOS doesn't have setitimer, but has set_alarm */
set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM);
#endif /* __BEOS__ */
/* Unlink myself from the wait queue, if on it (might not be anymore!) */ /* Unlink myself from the wait queue, if on it (might not be anymore!) */
LockLockTable(); LockLockTable();
...@@ -501,12 +490,6 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable, ...@@ -501,12 +490,6 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
bool early_deadlock = false; bool early_deadlock = false;
PROC *proc; PROC *proc;
int i; int i;
#ifndef __BEOS__
struct itimerval timeval,
dummy;
#else
bigtime_t time_interval;
#endif
/* /*
* Determine where to add myself in the wait queue. * Determine where to add myself in the wait queue.
...@@ -629,21 +612,9 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable, ...@@ -629,21 +612,9 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
* *
* By delaying the check until we've waited for a bit, we can avoid * By delaying the check until we've waited for a bit, we can avoid
* running the rather expensive deadlock-check code in most cases. * running the rather expensive deadlock-check code in most cases.
*
* Need to zero out struct to set the interval and the microseconds
* fields to 0.
*/ */
#ifndef __BEOS__ if (! enable_sigalrm_interrupt(DeadlockTimeout))
MemSet(&timeval, 0, sizeof(struct itimerval));
timeval.it_value.tv_sec = DeadlockTimeout / 1000;
timeval.it_value.tv_usec = (DeadlockTimeout % 1000) * 1000;
if (setitimer(ITIMER_REAL, &timeval, &dummy))
elog(FATAL, "ProcSleep: Unable to set timer for process wakeup"); elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
#else
time_interval = DeadlockTimeout * 1000000; /* usecs */
if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
#endif
/* /*
* If someone wakes us between SpinRelease and IpcSemaphoreLock, * If someone wakes us between SpinRelease and IpcSemaphoreLock,
...@@ -664,14 +635,8 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable, ...@@ -664,14 +635,8 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
/* /*
* Disable the timer, if it's still running * Disable the timer, if it's still running
*/ */
#ifndef __BEOS__ if (! disable_sigalrm_interrupt())
MemSet(&timeval, 0, sizeof(struct itimerval));
if (setitimer(ITIMER_REAL, &timeval, &dummy))
elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
#else
if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0)
elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup"); elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
#endif
/* /*
* Now there is nothing for LockWaitCancel to do. * Now there is nothing for LockWaitCancel to do.
...@@ -949,6 +914,69 @@ ProcSendSignal(BackendId procId) ...@@ -949,6 +914,69 @@ ProcSendSignal(BackendId procId)
} }
/*****************************************************************************
* SIGALRM interrupt support
*
* Maybe these should be in pqsignal.c?
*****************************************************************************/
/*
* Enable the SIGALRM interrupt to fire after the specified delay
*
* Delay is given in milliseconds. Caller should be sure a SIGALRM
* signal handler is installed before this is called.
*
* Returns TRUE if okay, FALSE on failure.
*/
bool
enable_sigalrm_interrupt(int delayms)
{
#ifndef __BEOS__
struct itimerval timeval,
dummy;
MemSet(&timeval, 0, sizeof(struct itimerval));
timeval.it_value.tv_sec = delayms / 1000;
timeval.it_value.tv_usec = (delayms % 1000) * 1000;
if (setitimer(ITIMER_REAL, &timeval, &dummy))
return false;
#else
/* BeOS doesn't have setitimer, but has set_alarm */
bigtime_t time_interval;
time_interval = delayms * 1000; /* usecs */
if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
return false;
#endif
return true;
}
/*
* Disable the SIGALRM interrupt, if it has not yet fired
*
* Returns TRUE if okay, FALSE on failure.
*/
bool
disable_sigalrm_interrupt(void)
{
#ifndef __BEOS__
struct itimerval timeval,
dummy;
MemSet(&timeval, 0, sizeof(struct itimerval));
if (setitimer(ITIMER_REAL, &timeval, &dummy))
return false;
#else
/* BeOS doesn't have setitimer, but has set_alarm */
if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0)
return false;
#endif
return true;
}
/***************************************************************************** /*****************************************************************************
* *
*****************************************************************************/ *****************************************************************************/
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.232 2001/09/08 01:10:20 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.233 2001/09/21 17:06:12 tgl Exp $
* *
* NOTES * NOTES
* this is the "main" module of the postgres backend and * this is the "main" module of the postgres backend and
...@@ -967,12 +967,12 @@ die(SIGNAL_ARGS) ...@@ -967,12 +967,12 @@ die(SIGNAL_ARGS)
} }
/* /*
* Shutdown signal from postmaster during client authentication. * Timeout or shutdown signal from postmaster during client authentication.
* Simply exit(0). * Simply exit(0).
* *
* XXX: possible future improvement: try to send a message indicating * XXX: possible future improvement: try to send a message indicating
* why we are disconnecting. Problem is to be sure we don't block while * why we are disconnecting. Problem is to be sure we don't block while
* doing so nor mess up the authentication message exchange. * doing so, nor mess up the authentication message exchange.
*/ */
void void
authdie(SIGNAL_ARGS) authdie(SIGNAL_ARGS)
...@@ -1168,16 +1168,6 @@ PostgresMain(int argc, char *argv[], ...@@ -1168,16 +1168,6 @@ PostgresMain(int argc, char *argv[],
SetProcessingMode(InitProcessing); SetProcessingMode(InitProcessing);
/*
* If under postmaster, initialize libpq and enable reporting of
* elog errors to the client.
*/
if (IsUnderPostmaster)
{
pq_init(); /* initialize libpq at backend startup */
whereToSendOutput = Remote; /* now safe to elog to client */
}
/* /*
* Set default values for command-line options. * Set default values for command-line options.
*/ */
...@@ -1736,7 +1726,7 @@ PostgresMain(int argc, char *argv[], ...@@ -1736,7 +1726,7 @@ PostgresMain(int argc, char *argv[],
if (!IsUnderPostmaster) if (!IsUnderPostmaster)
{ {
puts("\nPOSTGRES backend interactive interface "); puts("\nPOSTGRES backend interactive interface ");
puts("$Revision: 1.232 $ $Date: 2001/09/08 01:10:20 $\n"); puts("$Revision: 1.233 $ $Date: 2001/09/21 17:06:12 $\n");
} }
/* /*
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* Support for grand unified configuration scheme, including SET * Support for grand unified configuration scheme, including SET
* command, configuration file, and command line options. * command, configuration file, and command line options.
* *
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.50 2001/09/21 03:32:35 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.51 2001/09/21 17:06:12 tgl Exp $
* *
* Copyright 2000 by PostgreSQL Global Development Group * Copyright 2000 by PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>. * Written by Peter Eisentraut <peter_e@gmx.net>.
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
/* XXX these should be in other modules' header files */ /* XXX these should be in other modules' header files */
extern bool Log_connections; extern bool Log_connections;
extern int PreAuthDelay;
extern int AuthenticationTimeout;
extern int CheckPointTimeout; extern int CheckPointTimeout;
extern int CommitDelay; extern int CommitDelay;
extern int CommitSiblings; extern int CommitSiblings;
...@@ -320,6 +322,12 @@ static struct config_int ...@@ -320,6 +322,12 @@ static struct config_int
{"max_locks_per_transaction", PGC_POSTMASTER, &max_locks_per_xact, {"max_locks_per_transaction", PGC_POSTMASTER, &max_locks_per_xact,
64, 10, INT_MAX, NULL, NULL}, 64, 10, INT_MAX, NULL, NULL},
{"authentication_timeout", PGC_SIGHUP, &AuthenticationTimeout,
60, 1, 600, NULL, NULL},
{"pre_auth_delay", PGC_SIGHUP, &PreAuthDelay,
0, 0, 60, NULL, NULL},
{"checkpoint_segments", PGC_SIGHUP, &CheckPointSegments, {"checkpoint_segments", PGC_SIGHUP, &CheckPointSegments,
3, 1, INT_MAX, NULL, NULL}, 3, 1, INT_MAX, NULL, NULL},
......
...@@ -176,6 +176,7 @@ ...@@ -176,6 +176,7 @@
# #
#dynamic_library_path = '$libdir' #dynamic_library_path = '$libdir'
#australian_timezones = false #australian_timezones = false
#authentication_timeout = 60 # min 1, max 600
#deadlock_timeout = 1000 #deadlock_timeout = 1000
#default_transaction_isolation = 'read committed' #default_transaction_isolation = 'read committed'
#max_expr_depth = 10000 # min 10 #max_expr_depth = 10000 # min 10
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: proc.h,v 1.46 2001/09/07 00:27:30 tgl Exp $ * $Id: proc.h,v 1.47 2001/09/21 17:06:12 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -154,4 +154,7 @@ extern void ProcWaitForSignal(void); ...@@ -154,4 +154,7 @@ extern void ProcWaitForSignal(void);
extern void ProcCancelWaitForSignal(void); extern void ProcCancelWaitForSignal(void);
extern void ProcSendSignal(BackendId procId); extern void ProcSendSignal(BackendId procId);
extern bool enable_sigalrm_interrupt(int delayms);
extern bool disable_sigalrm_interrupt(void);
#endif /* PROC_H */ #endif /* PROC_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment