]> granicus.if.org Git - postgresql/commitdiff
Allow SSL configuration to be updated at SIGHUP.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 3 Jan 2017 02:37:12 +0000 (21:37 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 3 Jan 2017 02:37:12 +0000 (21:37 -0500)
It is no longer necessary to restart the server to enable, disable,
or reconfigure SSL.  Instead, we just create a new SSL_CTX struct
(by re-reading all relevant files) whenever we get SIGHUP.  Testing
shows that this is fast enough that it shouldn't be a problem.

In conjunction with that, downgrade the logic that complains about
pg_hba.conf "hostssl" lines when SSL isn't active: now that's just
a warning condition not an error.

An issue that still needs to be addressed is what shall we do with
passphrase-protected server keys?  As this stands, the server would
demand the passphrase again on every SIGHUP, which is certainly
impractical.  But the case was only barely supported before, so that
does not seem a sufficient reason to hold up committing this patch.

Andreas Karlsson, reviewed by Michael Banck and Michael Paquier

Discussion: https://postgr.es/m/556A6E8A.9030400@proxel.se

13 files changed:
doc/src/sgml/client-auth.sgml
doc/src/sgml/config.sgml
doc/src/sgml/runtime.sgml
src/backend/libpq/auth.c
src/backend/libpq/be-secure-openssl.c
src/backend/libpq/be-secure.c
src/backend/libpq/hba.c
src/backend/postmaster/postmaster.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/libpq/libpq-be.h
src/include/libpq/libpq.h
src/test/ssl/ServerSetup.pm

index 960f5b5871f3385fc03fe8fedca4c56aded703f3..dda5891900406c884dd9fa2650e658a47bd3437e 100644 (file)
@@ -156,9 +156,11 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
       <para>
        To make use of this option the server must be built with
        <acronym>SSL</acronym> support. Furthermore,
-       <acronym>SSL</acronym> must be enabled at server start time
+       <acronym>SSL</acronym> must be enabled
        by setting the <xref linkend="guc-ssl"> configuration parameter (see
        <xref linkend="ssl-tcp"> for more information).
+       Otherwise, the <literal>hostssl</literal> record is ignored except for
+       logging a warning that it cannot match any connections.
       </para>
      </listitem>
     </varlistentry>
index 8d7b3bfd663c1ec5071bb93ffb907adfa132d189..30dd54cd5d460a1b25bed3d6d39e3df640dd189f 100644 (file)
@@ -958,10 +958,10 @@ include_dir 'conf.d'
       <listitem>
        <para>
         Enables <acronym>SSL</> connections. Please read
-        <xref linkend="ssl-tcp"> before using this. The default
-        is <literal>off</>. This parameter can only be set at server
-        start.  <acronym>SSL</> communication is only possible with
-        TCP/IP connections.
+        <xref linkend="ssl-tcp"> before using this.
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line.
+        The default is <literal>off</>.
        </para>
       </listitem>
      </varlistentry>
@@ -975,11 +975,16 @@ include_dir 'conf.d'
       <listitem>
        <para>
         Specifies the name of the file containing the SSL server certificate
-        authority (CA).  The default is empty, meaning no CA file is loaded,
-        and client certificate verification is not performed.  (In previous
-        releases of PostgreSQL, the name of this file was hard-coded
-        as <filename>root.crt</filename>.)  Relative paths are relative to the
-        data directory.  This parameter can only be set at server start.
+        authority (CA).
+        Relative paths are relative to the data directory.
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line.
+        The default is empty, meaning no CA file is loaded,
+        and client certificate verification is not performed.
+       </para>
+       <para>
+        In previous releases of PostgreSQL, the name of this file was
+        hard-coded as <filename>root.crt</filename>.
        </para>
       </listitem>
      </varlistentry>
@@ -993,9 +998,10 @@ include_dir 'conf.d'
       <listitem>
        <para>
         Specifies the name of the file containing the SSL server certificate.
-        The default is <filename>server.crt</filename>.  Relative paths are
-        relative to the data directory.  This parameter can only be set at
-        server start.
+        Relative paths are relative to the data directory.
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line.
+        The default is <filename>server.crt</filename>.
        </para>
       </listitem>
      </varlistentry>
@@ -1009,11 +1015,15 @@ include_dir 'conf.d'
       <listitem>
        <para>
         Specifies the name of the file containing the SSL server certificate
-        revocation list (CRL).  The default is empty, meaning no CRL file is
-        loaded.  (In previous releases of PostgreSQL, the name of this file was
-        hard-coded as <filename>root.crl</filename>.)  Relative paths are
-        relative to the data directory.  This parameter can only be set at
-        server start.
+        revocation list (CRL).
+        Relative paths are relative to the data directory.
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line.
+        The default is empty, meaning no CRL file is loaded.
+       </para>
+       <para>
+        In previous releases of PostgreSQL, the name of this file was
+        hard-coded as <filename>root.crl</filename>.
        </para>
       </listitem>
      </varlistentry>
@@ -1027,9 +1037,10 @@ include_dir 'conf.d'
       <listitem>
        <para>
         Specifies the name of the file containing the SSL server private key.
-        The default is <filename>server.key</filename>.  Relative paths are
-        relative to the data directory.  This parameter can only be set at
-        server start.
+        Relative paths are relative to the data directory.
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line.
+        The default is <filename>server.key</filename>.
        </para>
       </listitem>
      </varlistentry>
@@ -1046,10 +1057,12 @@ include_dir 'conf.d'
         used on secure connections.  See
         the <citerefentry><refentrytitle>ciphers</></citerefentry> manual page
         in the <application>OpenSSL</> package for the syntax of this setting
-        and a list of supported values.  The default value is
-        <literal>HIGH:MEDIUM:+3DES:!aNULL</>.  It is usually reasonable,
-        unless you have specific security requirements.  This parameter can only
-        be set at server start.
+        and a list of supported values.
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line.
+        The default value is <literal>HIGH:MEDIUM:+3DES:!aNULL</>.  The
+        default is usually a reasonable choice unless you have specific
+        security requirements.
        </para>
 
        <para>
@@ -1113,7 +1126,7 @@ include_dir 'conf.d'
      </varlistentry>
 
      <varlistentry id="guc-ssl-prefer-server-ciphers" xreflabel="ssl_prefer_server_ciphers">
-      <term><varname>ssl_prefer_server_ciphers</varname> (<type>bool</type>)
+      <term><varname>ssl_prefer_server_ciphers</varname> (<type>boolean</type>)
       <indexterm>
        <primary><varname>ssl_prefer_server_ciphers</> configuration parameter</primary>
       </indexterm>
@@ -1121,8 +1134,10 @@ include_dir 'conf.d'
       <listitem>
        <para>
         Specifies whether to use the server's SSL cipher preferences, rather
-        than the client's.  The default is true.  This parameter can only be
-        set at server start.
+        than the client's.
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line.
+        The default is <literal>true</>.
        </para>
 
        <para>
@@ -1145,19 +1160,18 @@ include_dir 'conf.d'
        <para>
         Specifies the name of the curve to use in <acronym>ECDH</> key
         exchange.  It needs to be supported by all clients that connect.
-        It does not need to be same curve as used by server's Elliptic
-        Curve key.  The default is <literal>prime256v1</>.  This parameter
-        can only be set at server start.
+        It does not need to be the same curve used by the server's Elliptic
+        Curve key.
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line.
+        The default is <literal>prime256v1</>.
        </para>
 
        <para>
-        OpenSSL names for most common curves:
+        OpenSSL names for the most common curves are:
         <literal>prime256v1</> (NIST P-256),
         <literal>secp384r1</> (NIST P-384),
         <literal>secp521r1</> (NIST P-521).
-       </para>
-
-       <para>
         The full list of available curves can be shown with the command
         <command>openssl ecparam -list_curves</command>.  Not all of them
         are usable in <acronym>TLS</> though.
@@ -3003,7 +3017,7 @@ include_dir 'conf.d'
      </varlistentry>
 
      <varlistentry id="guc-track-commit-timestamp" xreflabel="track_commit_timestamp">
-      <term><varname>track_commit_timestamp</varname> (<type>bool</type>)
+      <term><varname>track_commit_timestamp</varname> (<type>boolean</type>)
       <indexterm>
        <primary><varname>track_commit_timestamp</> configuration parameter</primary>
       </indexterm>
index 787cfce9878099d088712b851e9a8a3978978283..65c7809332e3c6a54bcef0c354df770a22d02a74 100644 (file)
@@ -2285,11 +2285,20 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </table>
 
    <para>
-    The files <filename>server.key</>, <filename>server.crt</>,
-    <filename>root.crt</filename>, and <filename>root.crl</filename>
-    (or their configured alternative names)
-    are only examined during server start; so you must restart
-    the server for changes in them to take effect.
+    The server reads these files at server start and whenever the server
+    configuration is reloaded.  On <systemitem class="osname">Windows</>
+    systems, they are also re-read whenever a new backend process is spawned
+    for a new client connection.
+   </para>
+
+   <para>
+    If an error in these files is detected at server start, the server will
+    refuse to start.  But if an error is detected during a configuration
+    reload, the files are ignored and the old values continue to be used.
+    On <systemitem class="osname">Windows</> systems, if an error in these
+    files is detected at backend start, that backend will be unable to
+    establish an SSL connection.  In all these cases, the error condition is
+    reported in the server log.
    </para>
   </sect2>
 
index 72306e639cdaa2aa8fc93aea4fdfbb0572ac9dc0..da7ae16d502bd0e2d70d75949b450851fd727c9b 100644 (file)
@@ -348,28 +348,22 @@ ClientAuthentication(Port *port)
         */
        if (port->hba->clientcert)
        {
+               /* If we haven't loaded a root certificate store, fail */
+               if (!secure_loaded_verify_locations())
+                       ereport(FATAL,
+                                       (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                        errmsg("client certificates can only be checked if a root certificate store is available")));
+
                /*
-                * When we parse pg_hba.conf, we have already made sure that we have
-                * been able to load a certificate store. Thus, if a certificate is
-                * present on the client, it has been verified against our root
+                * If we loaded a root certificate store, and if a certificate is
+                * present on the client, then it has been verified against our root
                 * certificate store, and the connection would have been aborted
                 * already if it didn't verify ok.
                 */
-#ifdef USE_SSL
                if (!port->peer_cert_valid)
-               {
                        ereport(FATAL,
                                        (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                                  errmsg("connection requires a valid client certificate")));
-               }
-#else
-
-               /*
-                * hba.c makes sure hba->clientcert can't be set unless OpenSSL is
-                * present.
-                */
-               Assert(false);
-#endif
        }
 
        /*
index 668f217bba094e2d1aa43e07e617c982f8156ea2..4a39d7f74672c8d49c1b71e71e47c13cc131aebe 100644 (file)
@@ -77,12 +77,13 @@ static DH  *generate_dh_parameters(int prime_len, int generator);
 static DH  *tmp_dh_cb(SSL *s, int is_export, int keylength);
 static int     verify_cb(int, X509_STORE_CTX *);
 static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_ecdh(void);
+static bool initialize_ecdh(SSL_CTX *context, bool failOnError);
 static const char *SSLerrmessage(unsigned long ecode);
 
 static char *X509_NAME_to_cstring(X509_NAME *name);
 
 static SSL_CTX *SSL_context = NULL;
+static bool SSL_initialized = false;
 
 /* ------------------------------------------------------------ */
 /*                                              Hardcoded values                                               */
@@ -156,15 +157,20 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
 
 /*
  *     Initialize global SSL context.
+ *
+ * If failOnError is true, report any errors as FATAL (so we don't return).
+ * Otherwise, log errors at LOG level and return -1 to indicate trouble.
+ * Returns 0 if OK.
  */
-void
-be_tls_init(void)
+int
+be_tls_init(bool failOnError)
 {
-       struct stat buf;
-
        STACK_OF(X509_NAME) *root_cert_list = NULL;
+       SSL_CTX    *context;
+       struct stat buf;
 
-       if (!SSL_context)
+       /* This stuff need be done only once. */
+       if (!SSL_initialized)
        {
 #ifdef HAVE_OPENSSL_INIT_SSL
                OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
@@ -173,121 +179,157 @@ be_tls_init(void)
                SSL_library_init();
                SSL_load_error_strings();
 #endif
+               SSL_initialized = true;
+       }
 
-               /*
-                * We use SSLv23_method() because it can negotiate use of the highest
-                * mutually supported protocol version, while alternatives like
-                * TLSv1_2_method() permit only one specific version.  Note that we
-                * don't actually allow SSL v2 or v3, only TLS protocols (see below).
-                */
-               SSL_context = SSL_CTX_new(SSLv23_method());
-               if (!SSL_context)
-                       ereport(FATAL,
-                                       (errmsg("could not create SSL context: %s",
-                                                       SSLerrmessage(ERR_get_error()))));
+       /*
+        * We use SSLv23_method() because it can negotiate use of the highest
+        * mutually supported protocol version, while alternatives like
+        * TLSv1_2_method() permit only one specific version.  Note that we don't
+        * actually allow SSL v2 or v3, only TLS protocols (see below).
+        */
+       context = SSL_CTX_new(SSLv23_method());
+       if (!context)
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errmsg("could not create SSL context: %s",
+                                               SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
 
-               /*
-                * Disable OpenSSL's moving-write-buffer sanity check, because it
-                * causes unnecessary failures in nonblocking send cases.
-                */
-               SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+       /*
+        * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+        * unnecessary failures in nonblocking send cases.
+        */
+       SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 
-               /*
-                * Load and verify server's certificate and private key
-                */
-               if (SSL_CTX_use_certificate_chain_file(SSL_context,
-                                                                                          ssl_cert_file) != 1)
-                       ereport(FATAL,
-                                       (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                                 errmsg("could not load server certificate file \"%s\": %s",
-                                                ssl_cert_file, SSLerrmessage(ERR_get_error()))));
+       /*
+        * Load and verify server's certificate and private key
+        */
+       if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                errmsg("could not load server certificate file \"%s\": %s",
+                                               ssl_cert_file, SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
 
-               if (stat(ssl_key_file, &buf) != 0)
-                       ereport(FATAL,
-                                       (errcode_for_file_access(),
-                                        errmsg("could not access private key file \"%s\": %m",
-                                                       ssl_key_file)));
+       if (stat(ssl_key_file, &buf) != 0)
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errcode_for_file_access(),
+                                errmsg("could not access private key file \"%s\": %m",
+                                               ssl_key_file)));
+               goto error;
+       }
 
-               if (!S_ISREG(buf.st_mode))
-                       ereport(FATAL,
-                                       (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                                        errmsg("private key file \"%s\" is not a regular file",
-                                                       ssl_key_file)));
+       if (!S_ISREG(buf.st_mode))
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                errmsg("private key file \"%s\" is not a regular file",
+                                               ssl_key_file)));
+               goto error;
+       }
 
-               /*
-                * Refuse to load files owned by users other than us or root.
-                *
-                * XXX surely we can check this on Windows somehow, too.
-                */
+       /*
+        * Refuse to load files owned by users other than us or root.
+        *
+        * XXX surely we can check this on Windows somehow, too.
+        */
 #if !defined(WIN32) && !defined(__CYGWIN__)
-               if (buf.st_uid != geteuid() && buf.st_uid != 0)
-                       ereport(FATAL,
-                                       (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                                        errmsg("private key file \"%s\" must be owned by the database user or root",
-                                                       ssl_key_file)));
+       if (buf.st_uid != geteuid() && buf.st_uid != 0)
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                errmsg("private key file \"%s\" must be owned by the database user or root",
+                                               ssl_key_file)));
+               goto error;
+       }
 #endif
 
-               /*
-                * Require no public access to key file. If the file is owned by us,
-                * require mode 0600 or less. If owned by root, require 0640 or less
-                * to allow read access through our gid, or a supplementary gid that
-                * allows to read system-wide certificates.
-                *
-                * XXX temporarily suppress check when on Windows, because there may
-                * not be proper support for Unix-y file permissions.  Need to think
-                * of a reasonable check to apply on Windows.  (See also the data
-                * directory permission check in postmaster.c)
-                */
+       /*
+        * Require no public access to key file. If the file is owned by us,
+        * require mode 0600 or less. If owned by root, require 0640 or less to
+        * allow read access through our gid, or a supplementary gid that allows
+        * to read system-wide certificates.
+        *
+        * XXX temporarily suppress check when on Windows, because there may not
+        * be proper support for Unix-y file permissions.  Need to think of a
+        * reasonable check to apply on Windows.  (See also the data directory
+        * permission check in postmaster.c)
+        */
 #if !defined(WIN32) && !defined(__CYGWIN__)
-               if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
-                       (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
-                       ereport(FATAL,
-                                       (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                                 errmsg("private key file \"%s\" has group or world access",
-                                                ssl_key_file),
-                                        errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+       if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+               (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                errmsg("private key file \"%s\" has group or world access",
+                                               ssl_key_file),
+                                errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+               goto error;
+       }
 #endif
 
-               if (SSL_CTX_use_PrivateKey_file(SSL_context,
-                                                                               ssl_key_file,
-                                                                               SSL_FILETYPE_PEM) != 1)
-                       ereport(FATAL,
-                                       (errmsg("could not load private key file \"%s\": %s",
-                                                       ssl_key_file, SSLerrmessage(ERR_get_error()))));
-
-               if (SSL_CTX_check_private_key(SSL_context) != 1)
-                       ereport(FATAL,
-                                       (errmsg("check of private key failed: %s",
-                                                       SSLerrmessage(ERR_get_error()))));
+       if (SSL_CTX_use_PrivateKey_file(context,
+                                                                       ssl_key_file,
+                                                                       SSL_FILETYPE_PEM) != 1)
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                errmsg("could not load private key file \"%s\": %s",
+                                               ssl_key_file, SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
+
+       if (SSL_CTX_check_private_key(context) != 1)
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                errmsg("check of private key failed: %s",
+                                               SSLerrmessage(ERR_get_error()))));
+               goto error;
        }
 
        /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
-       SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
-       SSL_CTX_set_options(SSL_context,
+       SSL_CTX_set_tmp_dh_callback(context, tmp_dh_cb);
+       SSL_CTX_set_options(context,
                                                SSL_OP_SINGLE_DH_USE |
                                                SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 
        /* set up ephemeral ECDH keys */
-       initialize_ecdh();
+       if (!initialize_ecdh(context, failOnError))
+               goto error;
 
        /* set up the allowed cipher list */
-       if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
-               elog(FATAL, "could not set the cipher list (no valid ciphers available)");
+       if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                errmsg("could not set the cipher list (no valid ciphers available)")));
+               goto error;
+       }
 
        /* Let server choose order */
        if (SSLPreferServerCiphers)
-               SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+               SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
 
        /*
         * Load CA store, so we can verify client certificates if needed.
         */
        if (ssl_ca_file[0])
        {
-               if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
+               if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
                        (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
-                       ereport(FATAL,
-                                       (errmsg("could not load root certificate file \"%s\": %s",
+               {
+                       ereport(failOnError ? FATAL : LOG,
+                                       (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                        errmsg("could not load root certificate file \"%s\": %s",
                                                        ssl_ca_file, SSLerrmessage(ERR_get_error()))));
+                       goto error;
+               }
        }
 
        /*----------
@@ -297,7 +339,7 @@ be_tls_init(void)
         */
        if (ssl_crl_file[0])
        {
-               X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
+               X509_STORE *cvstore = SSL_CTX_get_cert_store(context);
 
                if (cvstore)
                {
@@ -310,15 +352,20 @@ be_tls_init(void)
                                                  X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
 #else
                                ereport(LOG,
-                               (errmsg("SSL certificate revocation list file \"%s\" ignored",
-                                               ssl_crl_file),
-                                errdetail("SSL library does not support certificate revocation lists.")));
+                                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                               errmsg("SSL certificate revocation list file \"%s\" ignored",
+                                          ssl_crl_file),
+                                                errdetail("SSL library does not support certificate revocation lists.")));
 #endif
                        }
                        else
-                               ereport(FATAL,
-                                               (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
+                       {
+                               ereport(failOnError ? FATAL : LOG,
+                                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                                errmsg("could not load SSL certificate revocation list file \"%s\": %s",
                                                         ssl_crl_file, SSLerrmessage(ERR_get_error()))));
+                               goto error;
+                       }
                }
        }
 
@@ -329,21 +376,53 @@ be_tls_init(void)
                 * presented.  We might fail such connections later, depending on what
                 * we find in pg_hba.conf.
                 */
-               SSL_CTX_set_verify(SSL_context,
+               SSL_CTX_set_verify(context,
                                                   (SSL_VERIFY_PEER |
                                                        SSL_VERIFY_CLIENT_ONCE),
                                                   verify_cb);
 
-               /* Set flag to remember CA store is successfully loaded */
-               ssl_loaded_verify_locations = true;
-
                /*
                 * Tell OpenSSL to send the list of root certs we trust to clients in
                 * CertificateRequests.  This lets a client with a keystore select the
                 * appropriate client certificate to send to us.
                 */
-               SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
+               SSL_CTX_set_client_CA_list(context, root_cert_list);
        }
+
+       /*
+        * Success!  Replace any existing SSL_context.
+        */
+       if (SSL_context)
+               SSL_CTX_free(SSL_context);
+
+       SSL_context = context;
+
+       /*
+        * Set flag to remember whether CA store has been loaded into SSL_context.
+        */
+       if (ssl_ca_file[0])
+               ssl_loaded_verify_locations = true;
+       else
+               ssl_loaded_verify_locations = false;
+
+       return 0;
+
+error:
+       if (context)
+               SSL_CTX_free(context);
+       return -1;
+}
+
+/*
+ *     Destroy global SSL context, if any.
+ */
+void
+be_tls_destroy(void)
+{
+       if (SSL_context)
+               SSL_CTX_free(SSL_context);
+       SSL_context = NULL;
+       ssl_loaded_verify_locations = false;
 }
 
 /*
@@ -360,6 +439,14 @@ be_tls_open_server(Port *port)
        Assert(!port->ssl);
        Assert(!port->peer);
 
+       if (!SSL_context)
+       {
+               ereport(COMMERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("could not initialize SSL connection: SSL context not set up")));
+               return -1;
+       }
+
        if (!(port->ssl = SSL_new(SSL_context)))
        {
                ereport(COMMERROR,
@@ -743,7 +830,7 @@ my_BIO_s_socket(void)
                        !BIO_meth_set_puts(my_bio_methods, BIO_meth_get_puts(biom)) ||
                        !BIO_meth_set_ctrl(my_bio_methods, BIO_meth_get_ctrl(biom)) ||
                        !BIO_meth_set_create(my_bio_methods, BIO_meth_get_create(biom)) ||
-                       !BIO_meth_set_destroy(my_bio_methods, BIO_meth_get_destroy(biom)) ||
+                !BIO_meth_set_destroy(my_bio_methods, BIO_meth_get_destroy(biom)) ||
                        !BIO_meth_set_callback_ctrl(my_bio_methods, BIO_meth_get_callback_ctrl(biom)))
                {
                        BIO_meth_free(my_bio_methods);
@@ -1034,8 +1121,8 @@ info_cb(const SSL *ssl, int type, int args)
        }
 }
 
-static void
-initialize_ecdh(void)
+static bool
+initialize_ecdh(SSL_CTX *context, bool failOnError)
 {
 #ifndef OPENSSL_NO_ECDH
        EC_KEY     *ecdh;
@@ -1043,18 +1130,28 @@ initialize_ecdh(void)
 
        nid = OBJ_sn2nid(SSLECDHCurve);
        if (!nid)
-               ereport(FATAL,
-                               (errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+               return false;
+       }
 
        ecdh = EC_KEY_new_by_curve_name(nid);
        if (!ecdh)
-               ereport(FATAL,
-                               (errmsg("ECDH: could not create key")));
+       {
+               ereport(failOnError ? FATAL : LOG,
+                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                errmsg("ECDH: could not create key")));
+               return false;
+       }
 
-       SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
-       SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+       SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
+       SSL_CTX_set_tmp_ecdh(context, ecdh);
        EC_KEY_free(ecdh);
 #endif
+
+       return true;
 }
 
 /*
index b267507de9a7bbe8e233d3a1ad93e59bdcef3fc3..4a6a0d6f589e07e0d4edcef72c15b42afec7de7b 100644 (file)
@@ -63,16 +63,31 @@ bool                SSLPreferServerCiphers;
 /* ------------------------------------------------------------ */
 
 /*
- *     Initialize global context
+ *     Initialize global context.
+ *
+ * If failOnError is true, report any errors as FATAL (so we don't return).
+ * Otherwise, log errors at LOG level and return -1 to indicate trouble.
+ * Returns 0 if OK.
  */
 int
-secure_initialize(void)
+secure_initialize(bool failOnError)
 {
 #ifdef USE_SSL
-       be_tls_init();
+       return be_tls_init(failOnError);
+#else
+       return 0;
 #endif
+}
 
-       return 0;
+/*
+ *     Destroy global context, if any.
+ */
+void
+secure_destroy(void)
+{
+#ifdef USE_SSL
+       be_tls_destroy();
+#endif
 }
 
 /*
index f1e9a38c92e6315d2b478884fc11d6b1eef370dd..5b644d64527292a7415a14484afdca910743468a 100644 (file)
@@ -870,28 +870,23 @@ parse_hba_line(List *line, int line_num, char *raw_line)
 
                if (token->string[4] == 's')    /* "hostssl" */
                {
-                       /* SSL support must be actually active, else complain */
+                       parsedline->conntype = ctHostSSL;
+                       /* Log a warning if SSL support is not active */
 #ifdef USE_SSL
-                       if (EnableSSL)
-                               parsedline->conntype = ctHostSSL;
-                       else
-                       {
+                       if (!EnableSSL)
                                ereport(LOG,
                                                (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                                                errmsg("hostssl requires SSL to be turned on"),
+                               errmsg("hostssl record cannot match because SSL is disabled"),
                                                 errhint("Set ssl = on in postgresql.conf."),
                                                 errcontext("line %d of configuration file \"%s\"",
                                                                        line_num, HbaFileName)));
-                               return NULL;
-                       }
 #else
                        ereport(LOG,
                                        (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                                        errmsg("hostssl is not supported by this build"),
+                                        errmsg("hostssl record cannot match because SSL is not supported by this build"),
                          errhint("Compile with --with-openssl to use SSL connections."),
                                         errcontext("line %d of configuration file \"%s\"",
                                                                line_num, HbaFileName)));
-                       return NULL;
 #endif
                }
                else if (token->string[4] == 'n')               /* "hostnossl" */
@@ -1417,10 +1412,6 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
        }
        else if (strcmp(name, "clientcert") == 0)
        {
-               /*
-                * Since we require ctHostSSL, this really can never happen on
-                * non-SSL-enabled builds, so don't bother checking for USE_SSL.
-                */
                if (hbaline->conntype != ctHostSSL)
                {
                        ereport(LOG,
@@ -1432,16 +1423,6 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
                }
                if (strcmp(val, "1") == 0)
                {
-                       if (!secure_loaded_verify_locations())
-                       {
-                               ereport(LOG,
-                                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                                                errmsg("client certificates can only be checked if a root certificate store is available"),
-                                                errhint("Make sure the configuration parameter \"%s\" is set.", "ssl_ca_file"),
-                                                errcontext("line %d of configuration file \"%s\"",
-                                                                       line_num, HbaFileName)));
-                               return false;
-                       }
                        hbaline->clientcert = true;
                }
                else
index 535f6c4e5a05ad2de860f89ae1a7ed770a3b49b9..02e6bb9dbdc8cc183006f47a27a053431aad86f6 100644 (file)
@@ -368,6 +368,11 @@ static unsigned int random_seed = 0;
 static struct timeval random_start_time;
 #endif
 
+#ifdef USE_SSL
+/* Set when and if SSL has been initialized properly */
+static bool LoadedSSL = false;
+#endif
+
 #ifdef USE_BONJOUR
 static DNSServiceRef bonjour_sdref = NULL;
 #endif
@@ -930,7 +935,10 @@ PostmasterMain(int argc, char *argv[])
         */
 #ifdef USE_SSL
        if (EnableSSL)
-               secure_initialize();
+       {
+               (void) secure_initialize(true);
+               LoadedSSL = true;
+       }
 #endif
 
        /*
@@ -1961,7 +1969,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
 
 #ifdef USE_SSL
                /* No SSL when disabled or on Unix sockets */
-               if (!EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
+               if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
                        SSLok = 'N';
                else
                        SSLok = 'S';            /* Support for SSL */
@@ -2498,13 +2506,30 @@ SIGHUP_handler(SIGNAL_ARGS)
 
                /* Reload authentication config files too */
                if (!load_hba())
-                       ereport(WARNING,
+                       ereport(LOG,
                                        (errmsg("pg_hba.conf not reloaded")));
 
                if (!load_ident())
-                       ereport(WARNING,
+                       ereport(LOG,
                                        (errmsg("pg_ident.conf not reloaded")));
 
+#ifdef USE_SSL
+               /* Reload SSL configuration as well */
+               if (EnableSSL)
+               {
+                       if (secure_initialize(false) == 0)
+                               LoadedSSL = true;
+                       else
+                               ereport(LOG,
+                                               (errmsg("SSL context not reloaded")));
+               }
+               else
+               {
+                       secure_destroy();
+                       LoadedSSL = false;
+               }
+#endif
+
 #ifdef EXEC_BACKEND
                /* Update the starting-point file for future children */
                write_nondefault_variables(PGC_SIGHUP);
@@ -4733,12 +4758,22 @@ SubPostmasterMain(int argc, char *argv[])
                 * context structures contain function pointers and cannot be passed
                 * through the parameter file.
                 *
+                * If for some reason reload fails (maybe the user installed broken
+                * key files), soldier on without SSL; that's better than all
+                * connections becoming impossible.
+                *
                 * XXX should we do this in all child processes?  For the moment it's
                 * enough to do it in backend children.
                 */
 #ifdef USE_SSL
                if (EnableSSL)
-                       secure_initialize();
+               {
+                       if (secure_initialize(false) == 0)
+                               LoadedSSL = true;
+                       else
+                               ereport(LOG,
+                                               (errmsg("SSL context could not be reloaded in child process")));
+               }
 #endif
 
                /*
index 946ba9e73ebd89caeecff3d05dbc04bfdcce0370..a5963b3d55a1fbbf7d82cf19f9d0ac9428ac88a7 100644 (file)
@@ -934,7 +934,7 @@ static struct config_bool ConfigureNamesBool[] =
                NULL, NULL, NULL
        },
        {
-               {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+               {"ssl", PGC_SIGHUP, CONN_AUTH_SECURITY,
                        gettext_noop("Enables SSL connections."),
                        NULL
                },
@@ -943,7 +943,7 @@ static struct config_bool ConfigureNamesBool[] =
                check_ssl, NULL, NULL
        },
        {
-               {"ssl_prefer_server_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+               {"ssl_prefer_server_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
                        gettext_noop("Give priority to server ciphersuite order."),
                        NULL
                },
@@ -2304,7 +2304,7 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_XBLOCKS
                },
                &WalWriterFlushAfter,
-               (1024*1024) / XLOG_BLCKSZ, 0, INT_MAX,
+               (1024 * 1024) / XLOG_BLCKSZ, 0, INT_MAX,
                NULL, NULL, NULL
        },
 
@@ -3435,7 +3435,7 @@ static struct config_string ConfigureNamesString[] =
        },
 
        {
-               {"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+               {"ssl_cert_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
                        gettext_noop("Location of the SSL server certificate file."),
                        NULL
                },
@@ -3445,7 +3445,7 @@ static struct config_string ConfigureNamesString[] =
        },
 
        {
-               {"ssl_key_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+               {"ssl_key_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
                        gettext_noop("Location of the SSL server private key file."),
                        NULL
                },
@@ -3455,7 +3455,7 @@ static struct config_string ConfigureNamesString[] =
        },
 
        {
-               {"ssl_ca_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+               {"ssl_ca_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
                        gettext_noop("Location of the SSL certificate authority file."),
                        NULL
                },
@@ -3465,7 +3465,7 @@ static struct config_string ConfigureNamesString[] =
        },
 
        {
-               {"ssl_crl_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+               {"ssl_crl_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
                        gettext_noop("Location of the SSL certificate revocation list file."),
                        NULL
                },
@@ -3507,7 +3507,7 @@ static struct config_string ConfigureNamesString[] =
        },
 
        {
-               {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+               {"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
                        gettext_noop("Sets the list of allowed SSL ciphers."),
                        NULL,
                        GUC_SUPERUSER_ONLY
@@ -3522,7 +3522,7 @@ static struct config_string ConfigureNamesString[] =
        },
 
        {
-               {"ssl_ecdh_curve", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+               {"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SECURITY,
                        gettext_noop("Sets the curve to use for ECDH."),
                        NULL,
                        GUC_SUPERUSER_ONLY
index ee8232f2f40ba59218361a26eea90782286cc027..b3f29610d070e408c2128728a057b78cd63f6199 100644 (file)
 # - Security and Authentication -
 
 #authentication_timeout = 1min         # 1s-600s
-#ssl = off                             # (change requires restart)
+#ssl = off
 #ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
-                                       # (change requires restart)
-#ssl_prefer_server_ciphers = on                # (change requires restart)
-#ssl_ecdh_curve = 'prime256v1'         # (change requires restart)
-#ssl_cert_file = 'server.crt'          # (change requires restart)
-#ssl_key_file = 'server.key'           # (change requires restart)
-#ssl_ca_file = ''                      # (change requires restart)
-#ssl_crl_file = ''                     # (change requires restart)
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+#ssl_cert_file = 'server.crt'
+#ssl_key_file = 'server.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
 #password_encryption = md5             # md5 or plain
 #db_user_namespace = off
 #row_security = on
index 66647ad003260c01d7ab7af61dcf6b56f88361cb..5dac9ceb183c56399330485c881cba12dcc6139c 100644 (file)
@@ -199,7 +199,8 @@ typedef struct Port
  * These functions are implemented by the glue code specific to each
  * SSL implementation (e.g. be-secure-openssl.c)
  */
-extern void be_tls_init(void);
+extern int     be_tls_init(bool failOnError);
+extern void be_tls_destroy(void);
 extern int     be_tls_open_server(Port *port);
 extern void be_tls_close(Port *port);
 extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
index 5fac8171ed8185d0270028abca325dee95695f8e..66ceb2b4a0bac3c64b91db0fe1a03f7c28b8d510 100644 (file)
@@ -81,7 +81,7 @@ extern char *ssl_key_file;
 extern char *ssl_ca_file;
 extern char *ssl_crl_file;
 
-extern int     secure_initialize(void);
+extern int     secure_initialize(bool failOnError);
 extern bool secure_loaded_verify_locations(void);
 extern void secure_destroy(void);
 extern int     secure_open_server(Port *port);
index d312880f8b1a5ed6763ff54f55e7f62afe6e728e..20eaf76bffc074499832f04fb66bc1e4a302059c 100644 (file)
@@ -70,7 +70,11 @@ sub configure_test_server_for_ssl
 
        close CONF;
 
-# Copy all server certificates and keys, and client root cert, to the data dir
+       # ssl configuration will be placed here
+       open SSLCONF, ">$pgdata/sslconfig.conf";
+       close SSLCONF;
+
+       # Copy all server certificates and keys, and client root cert, to the data dir
        copy_files("ssl/server-*.crt", $pgdata);
        copy_files("ssl/server-*.key", $pgdata);
        chmod(0600, glob "$pgdata/server-*.key") or die $!;
@@ -78,25 +82,14 @@ sub configure_test_server_for_ssl
        copy_files("ssl/root_ca.crt", $pgdata);
        copy_files("ssl/root+client.crl",    $pgdata);
 
-  # Only accept SSL connections from localhost. Our tests don't depend on this
-  # but seems best to keep it as narrow as possible for security reasons.
-  #
-  # When connecting to certdb, also check the client certificate.
-       open HBA, ">$pgdata/pg_hba.conf";
-       print HBA
-"# TYPE  DATABASE        USER            ADDRESS                 METHOD\n";
-       print HBA
-"hostssl trustdb         ssltestuser     $serverhost/32            trust\n";
-       print HBA
-"hostssl trustdb         ssltestuser     ::1/128                 trust\n";
-       print HBA
-"hostssl certdb          ssltestuser     $serverhost/32            cert\n";
-       print HBA
-"hostssl certdb          ssltestuser     ::1/128                 cert\n";
-       close HBA;
+       # Stop and restart server to load new listen_addresses.
+       $node->restart;
+
+       # Change pg_hba after restart because hostssl requires ssl=on
+       configure_hba_for_ssl($node, $serverhost);
 }
 
-# Change the configuration to use given server cert file, and restart
+# Change the configuration to use given server cert file, and reload
 # the server so that the configuration takes effect.
 sub switch_server_cert
 {
@@ -105,7 +98,7 @@ sub switch_server_cert
        my $cafile = $_[2] || "root+client_ca";
        my $pgdata   = $node->data_dir;
 
-       diag "Restarting server with certfile \"$certfile\" and cafile \"$cafile\"...";
+       diag "Reloading server with certfile \"$certfile\" and cafile \"$cafile\"...";
 
        open SSLCONF, ">$pgdata/sslconfig.conf";
        print SSLCONF "ssl=on\n";
@@ -115,6 +108,29 @@ sub switch_server_cert
        print SSLCONF "ssl_crl_file='root+client.crl'\n";
        close SSLCONF;
 
-       # Stop and restart server to reload the new config.
-       $node->restart;
+       $node->reload;
+}
+
+sub configure_hba_for_ssl
+{
+       my $node       = $_[0];
+       my $serverhost = $_[1];
+       my $pgdata     = $node->data_dir;
+
+       # Only accept SSL connections from localhost. Our tests don't depend on this
+       # but seems best to keep it as narrow as possible for security reasons.
+       #
+       # When connecting to certdb, also check the client certificate.
+       open HBA, ">$pgdata/pg_hba.conf";
+       print HBA
+"# TYPE  DATABASE        USER            ADDRESS                 METHOD\n";
+       print HBA
+"hostssl trustdb         ssltestuser     $serverhost/32            trust\n";
+       print HBA
+"hostssl trustdb         ssltestuser     ::1/128                 trust\n";
+       print HBA
+"hostssl certdb          ssltestuser     $serverhost/32            cert\n";
+       print HBA
+"hostssl certdb          ssltestuser     ::1/128                 cert\n";
+       close HBA;
 }