Add an overall timeout on the client authentication cycle, so that
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 21 Sep 2001 17:06:12 +0000 (17:06 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 21 Sep 2001 17:06:12 +0000 (17:06 +0000)
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?

doc/src/sgml/runtime.sgml
src/backend/libpq/pqsignal.c
src/backend/postmaster/postmaster.c
src/backend/storage/lmgr/proc.c
src/backend/tcop/postgres.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/storage/proc.h

index 55034494dcb60a208ecd522255dc1e2125c7c1df..e6095f26996fa9ea070d5854bab230ba40747f47 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$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">
@@ -1018,6 +1018,20 @@ env PGOPTIONS='-c geqo=off' psql
       </listitem>
      </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>
       <indexterm>
        <primary>deadlock</primary>
index effe2e09cfc1c146b1196ee9068e1a3b01c60ab1..d8c7a1d852ca278812e8e123dd17884de96e1112 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * 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
  *     This shouldn't be in libpq, but the monitor and some other
@@ -53,7 +53,7 @@
  * signals that should never be turned off.
  *
  * 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
  * signals (is this ever nonzero??)
@@ -109,6 +109,9 @@ pqinitmask(void)
 #ifdef SIGQUIT
    sigdelset(&AuthBlockSig, SIGQUIT);
 #endif
+#ifdef SIGALRM
+   sigdelset(&AuthBlockSig, SIGALRM);
+#endif
 #else
    UnBlockSig = 0;
    BlockSig = sigmask(SIGHUP) | sigmask(SIGQUIT) |
@@ -116,7 +119,7 @@ pqinitmask(void)
        sigmask(SIGINT) | sigmask(SIGUSR1) |
        sigmask(SIGUSR2) | sigmask(SIGCHLD) |
        sigmask(SIGWINCH) | sigmask(SIGFPE);
-   AuthBlockSig = sigmask(SIGHUP) | sigmask(SIGALRM) |
+   AuthBlockSig = sigmask(SIGHUP) |
        sigmask(SIGINT) | sigmask(SIGUSR1) |
        sigmask(SIGUSR2) | sigmask(SIGCHLD) |
        sigmask(SIGWINCH) | sigmask(SIGFPE);
index b1e6bc23b2fc1897af0862150217e2d0dfe635f0..e8c9ae70efd5161a84ff6c50d94c2169e5d156a8 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * 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
  *
@@ -190,6 +190,8 @@ bool        NetServer = false;  /* listen on TCP/IP */
 bool       EnableSSL = false;
 bool       SilentMode = false; /* silent mode (-S) */
 
+int            PreAuthDelay = 0;
+int            AuthenticationTimeout = 60;
 int            CheckPointTimeout = 300;
 
 /* Startup/shutdown state */
@@ -1941,16 +1943,38 @@ DoBackend(Port *port)
    /* Reset MyProcPid to new backend's pid */
    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
     * during any client authentication related communication. Otherwise
     * 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(SIGQUIT, authdie);
+   pqsignal(SIGALRM, authdie);
    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
     * request packet); then perform client authentication.
@@ -1963,9 +1987,11 @@ DoBackend(Port *port)
    ClientAuthentication(MyProcPort); /* might not return, if failure */
 
    /*
-    * Done with authentication.  Prevent SIGTERM/SIGQUIT again until
-    * backend startup is complete.
+    * Done with authentication.  Disable timeout, and prevent SIGTERM/SIGQUIT
+    * again until backend startup is complete.
     */
+   if (! disable_sigalrm_interrupt())
+       elog(FATAL, "DoBackend: Unable to disable timer for auth timeout");
    PG_SETMASK(&BlockSig);
 
    /*
index 00c1f59c07f7612d7d067ddbf827c33ab2205454..0a02e6f006c0b38bd72e481faa7dede4be3dfb92 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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)
    waitingForLock = false;
 
    /* Turn off the deadlock timer, if it's still running (see ProcSleep) */
-#ifndef __BEOS__
-   {
-       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__ */
+   disable_sigalrm_interrupt();
 
    /* Unlink myself from the wait queue, if on it (might not be anymore!) */
    LockLockTable();
@@ -501,12 +490,6 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
    bool        early_deadlock = false;
    PROC       *proc;
    int         i;
-#ifndef __BEOS__
-   struct itimerval timeval,
-               dummy;
-#else
-   bigtime_t   time_interval;
-#endif
 
    /*
     * Determine where to add myself in the wait queue.
@@ -629,21 +612,9 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
     *
     * By delaying the check until we've waited for a bit, we can avoid
     * 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__
-   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))
+   if (! enable_sigalrm_interrupt(DeadlockTimeout))
        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,
@@ -664,14 +635,8 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
    /*
     * Disable the timer, if it's still running
     */
-#ifndef __BEOS__
-   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)
+   if (! disable_sigalrm_interrupt())
        elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
-#endif
 
    /*
     * Now there is nothing for LockWaitCancel to do.
@@ -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;
+}
+
+
 /*****************************************************************************
  *
  *****************************************************************************/
index 3aa28e1dbe08fe7bd9a95e89e32411457c97e2ce..feb8aea129fd134b9ebf62fd8d9367c437622f95 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *   this is the "main" module of the postgres backend and
@@ -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).
  *
  * XXX: possible future improvement: try to send a message indicating
  * 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
 authdie(SIGNAL_ARGS)
@@ -1168,16 +1168,6 @@ PostgresMain(int argc, char *argv[],
 
    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.
     */
@@ -1736,7 +1726,7 @@ PostgresMain(int argc, char *argv[],
    if (!IsUnderPostmaster)
    {
        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");
    }
 
    /*
index 5a5dcac47a60decc8f9ee702f83a101e4f63617e..00fc0bebd2d10aae67bb76d225a5222080c8fda2 100644 (file)
@@ -4,7 +4,7 @@
  * Support for grand unified configuration scheme, including SET
  * 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
  * Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -41,6 +41,8 @@
 
 /* XXX these should be in other modules' header files */
 extern bool Log_connections;
+extern int PreAuthDelay;
+extern int AuthenticationTimeout;
 extern int CheckPointTimeout;
 extern int CommitDelay;
 extern int CommitSiblings;
@@ -320,6 +322,12 @@ static struct config_int
    {"max_locks_per_transaction", PGC_POSTMASTER, &max_locks_per_xact,
     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,
    3, 1, INT_MAX, NULL, NULL},
 
index c39e31d7483b3c977d65f4a51c5e4cb97ba59f05..b17af7d1fa709dd7f69b661ba30c12cb0f3269b2 100644 (file)
 #
 #dynamic_library_path = '$libdir'
 #australian_timezones = false
+#authentication_timeout = 60  # min 1, max 600
 #deadlock_timeout = 1000
 #default_transaction_isolation = 'read committed'
 #max_expr_depth = 10000 # min 10
index 0b318ec0a54f01be8a88378e343009b4abdde656..9a1c63ef1be22411f6598efaa9b3a0a30e52ff68 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * 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);
 extern void ProcCancelWaitForSignal(void);
 extern void ProcSendSignal(BackendId procId);
 
+extern bool enable_sigalrm_interrupt(int delayms);
+extern bool disable_sigalrm_interrupt(void);
+
 #endif  /* PROC_H */