OSDN Git Service

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 5503449..e6095f2 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">
@@ -1019,6 +1019,20 @@ env PGOPTIONS='-c geqo=off' psql
      </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>
        <secondary>timeout</secondary>
index effe2e0..d8c7a1d 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 b1e6bc2..e8c9ae7 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 */
@@ -1942,15 +1944,37 @@ DoBackend(Port *port)
        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 00c1f59..0a02e6f 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.
@@ -950,6 +915,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 3aa28e1..feb8aea 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)
@@ -1169,16 +1169,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.
         */
        Noversion = false;
@@ -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 5a5dcac..00fc0be 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 c39e31d..b17af7d 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 0b318ec..9a1c63e 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 */