Re: SSL enhancement patch ver.2 - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: SSL enhancement patch ver.2
Date
Msg-id 200702160300.l1G30b725681@momjian.us
Whole thread Raw
In response to SSL enhancement patch ver.2  ("Victor B. Wagner" <vitus@cryptocom.ru>)
List pgsql-patches
Patch applied --- SSL improvements:

        o read global SSL configuration file
        o add GUC "ssl_ciphers" to control allowed ciphers
        o add libpq environment variable PGSSLKEY to control SSL
      hardware keys

I adjusted the documentation wording and some of the single-letter
variable names you used --- the applied verison is attached.  Thanks.

---------------------------------------------------------------------------

Victor B. Wagner wrote:
> This patch adds following functionality to PostgreSQL
>
> 1. If PostgreSQL is compiled with OpenSSL version 0.9.7 and above,
> both backend and libpq read site-wide OpenSSL configuration file as
> described in OPENSSL_config functon manual page.
>
> This allows to use hardware crypto acceleration modules (engines) and,
> in future version 0.9.9 would allow to use additional cryptoalgorithms
> (i.e. national standards) which are not included in core OpenSSL.
>
> All other configuration parameters which are supported by OpenSSL
> library also are taken into account.
>
>
> 2. New configuration option "ssl_ciphers" is added to postgresql.conf.
> This option allows to change list of ciphers, acceptable by backend
> during SSL connection. Changing list of ciphers can be desirable to
> tighten or relax security of particular installation, and allows quick
> fix on configuration file level in case if vulnerability is discovered
> in one of cryptoalgorithms or their OpenSSL implementation - cipher
> suites which use such algorithm can be easily disabled.
>
>
> 3. If libpq compiled with OpenSSL 0.9.7 and above, compiled with engine
> support, it is possible to store secret key of client certificate on the
> hardware token, supported by one of OpenSSL engines (Hardware Security
> Module). Name of engine which supports token and engine-specific key ID
> are specifyed using environment variable PGSSLKEY.
>
> This allows use of hardware tokens such as smartcards to identify
> clients, connecting to database.
>
> This functionality can be used in installations with high security
> requirements or in situations where several people can use same terminal
> (such as cash register in shops or malls).
>
> If PostgreSQL is compiled with version of OpenSSL which do not support
> engines or doesn't have OPENSSL_config function, related functionality
> is excluded by preprocessor conditionals, based on value of
> SSLEAY_VERSION_NUMBER preprocessor symbol which is defined by all
> versions of OpenSSL.
>

[ Attachment, skipping... ]

>
> ---------------------------(end of broadcast)---------------------------
> TIP 5: don't forget to increase your free space map settings

--
  Bruce Momjian  <bruce@momjian.us>          http://momjian.us
  EnterpriseDB                               http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: doc/src/sgml/config.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/config.sgml,v
retrieving revision 1.110
diff -c -c -r1.110 config.sgml
*** doc/src/sgml/config.sgml    8 Feb 2007 15:46:03 -0000    1.110
--- doc/src/sgml/config.sgml    16 Feb 2007 01:26:20 -0000
***************
*** 569,574 ****
--- 569,588 ----
        </listitem>
       </varlistentry>

+      <varlistentry id="guc-ssl-ciphers" xreflabel="ssl-ciphers">
+       <term><varname>ssl_ciphers> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>ssl_ciphers</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies a list of <acronym>SSL</> ciphers which can be used to
+         establish secure connections. See the <application>openssl</>
+         manual page for a list of supported ciphers.
+        </para>
+       </listitem>
+      </varlistentry>
+
       <varlistentry id="guc-password-encryption" xreflabel="password_encryption">
        <term><varname>password_encryption</varname> (<type>boolean</type>)</term>
        <indexterm>
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.228
diff -c -c -r1.228 libpq.sgml
*** doc/src/sgml/libpq.sgml    6 Feb 2007 03:03:11 -0000    1.228
--- doc/src/sgml/libpq.sgml    16 Feb 2007 01:26:22 -0000
***************
*** 4175,4180 ****
--- 4175,4192 ----
  <listitem>
  <para>
  <indexterm>
+ <primary><envar>PGSSLKEY</envar></primary>
+ </indexterm>
+ <envar>PGSSLKEY</envar>
+ specifies the hardware token which stores the secret key for the client
+ certificate, instead of a file. The value of this variable should consist
+ of a colon-separated engine name (engines are <productname>OpenSSL</>
+ loadable modules) and an engine-specific key identifier.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <indexterm>
   <primary><envar>PGKRBSRVNAME</envar></primary>
  </indexterm>
  <envar>PGKRBSRVNAME</envar> sets the Kerberos service name to use when
***************
*** 4438,4457 ****
     for increased security. See <xref linkend="ssl-tcp"> for details
     about the server-side <acronym>SSL</> functionality.
    </para>
!
    <para>
     If the server demands a client certificate,
     <application>libpq</application>
     will send the certificate stored in file
     <filename>~/.postgresql/postgresql.crt</> within the user's home directory.
     A matching private key file <filename>~/.postgresql/postgresql.key</>
!    must also be present, and must not be world-readable.
     (On Microsoft Windows these files are named
     <filename>%APPDATA%\postgresql\postgresql.crt</filename> and
     <filename>%APPDATA%\postgresql\postgresql.key</filename>.)
    </para>

    <para>
     If the file <filename>~/.postgresql/root.crt</> is present in the user's
     home directory,
     <application>libpq</application> will use the certificate list stored
--- 4450,4494 ----
     for increased security. See <xref linkend="ssl-tcp"> for details
     about the server-side <acronym>SSL</> functionality.
    </para>
!   <para>
!   <application>libpq</application> reads the system-wide
!   <productname>OpenSSL</productname> configuration file. By default, this
!   file is named <filename>openssl.cnf</filename> and is located in the
!   directory reported by <application>openssl</>:
!   <programlisting>
!   openssl version -d
!   </programlisting>
!   The default can be overriden by setting environment variable
!   <envar>OPENSSL_CONF</envar> to the name of the desired configuration
!   file.
!   </para>
    <para>
     If the server demands a client certificate,
     <application>libpq</application>
     will send the certificate stored in file
     <filename>~/.postgresql/postgresql.crt</> within the user's home directory.
     A matching private key file <filename>~/.postgresql/postgresql.key</>
!    must also be present, and must not be world-readable, unless the secret
!    key is stored in a hardware token, as specified by
!    <envar>PGSSLKEY</envar>.
     (On Microsoft Windows these files are named
     <filename>%APPDATA%\postgresql\postgresql.crt</filename> and
     <filename>%APPDATA%\postgresql\postgresql.key</filename>.)
    </para>

    <para>
+    If the environment variable <envar>PGSSLKEY</envar> is set, its value
+    should consist of a colon-separated engine name and key identifier. In
+    this case, <application>libpq</application> will load the specified
+    engine, i.e. the <productname>OpenSSL</> module which supports special
+    hardware and reference the key with the specified identifier.
+    Identifiers are engine-specific. Typically, cryptography hardware tokens
+    do not reveal secret keys to the application. Instead, applications
+    delegate all cryptography operations which require the secret key to
+    the hardware token.
+   </para>
+
+   <para>
     If the file <filename>~/.postgresql/root.crt</> is present in the user's
     home directory,
     <application>libpq</application> will use the certificate list stored
Index: doc/src/sgml/runtime.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v
retrieving revision 1.376
diff -c -c -r1.376 runtime.sgml
*** doc/src/sgml/runtime.sgml    1 Feb 2007 00:28:18 -0000    1.376
--- doc/src/sgml/runtime.sgml    16 Feb 2007 01:26:23 -0000
***************
*** 1516,1521 ****
--- 1516,1540 ----
    </para>

    <para>
+    <productname>OpenSSL</productname> supports a wide range of ciphers
+    and authentication algorithms, whose strength varies significantly.
+    You can restrict the list of ciphers which can be used to connect to
+    your server using the <xref linkend="guc-ssl-ciphers"> parameter.
+   </para>
+
+   <para>
+    <productname>PostgreSQL</productname> reads a system-wide
+    <productname>OpenSSL</productname> configuration file. By default this
+    file is named <filename>openssl.cnf</filename> and is located in the
+    directory reported by <application>openssl</>:
+    <programlisting>
+    openssl version -d
+    </programlisting>
+    This default can be overriden by setting environment variable
+    <envar>OPENSSL_CONF</envar> to the name of desired configuration file.
+   </para>
+
+   <para>
     For details on how to create your server private key and certificate,
     refer to the <productname>OpenSSL</> documentation. A
     self-signed certificate can be used for testing, but a
***************
*** 1528,1535 ****
  <programlisting>
  openssl req -new -text -out server.req
  </programlisting>
!    Fill out the information that <command>openssl</> asks for. Make sure
!    that you enter the local host name as <quote>Common Name</>; the challenge
     password can be left blank. The program will generate a key that is
     passphrase protected; it will not accept a passphrase that is less
     than four characters long. To remove the passphrase (as you must if
--- 1547,1554 ----
  <programlisting>
  openssl req -new -text -out server.req
  </programlisting>
!    Fill out the information that <application>openssl</> asks for. Make sure
!    you enter the local host name as <quote>Common Name</>; the challenge
     password can be left blank. The program will generate a key that is
     passphrase protected; it will not accept a passphrase that is less
     than four characters long. To remove the passphrase (as you must if
Index: src/backend/libpq/be-secure.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v
retrieving revision 1.77
diff -c -c -r1.77 be-secure.c
*** src/backend/libpq/be-secure.c    7 Feb 2007 00:52:35 -0000    1.77
--- src/backend/libpq/be-secure.c    16 Feb 2007 01:26:24 -0000
***************
*** 92,97 ****
--- 92,101 ----
  #ifdef USE_SSL
  #include <openssl/ssl.h>
  #include <openssl/dh.h>
+ #if SSLEAY_VERSION_NUMBER >= 0x0907000L
+ #include <openssl/conf.h>
+ #endif
+
  #endif

  #include "libpq/libpq.h"
***************
*** 125,130 ****
--- 129,138 ----
  #define RENEGOTIATION_LIMIT (512 * 1024 * 1024)

  static SSL_CTX *SSL_context = NULL;
+
+ /* GUC variable controlling SSL cipher list*/
+ extern char *SSLCipherSuites;
+
  #endif

  /* ------------------------------------------------------------ */
***************
*** 719,724 ****
--- 727,735 ----

      if (!SSL_context)
      {
+ #if SSLEAY_VERSION_NUMBER >= 0x0907000L
+         OPENSSL_config(NULL);
+ #endif
          SSL_library_init();
          SSL_load_error_strings();
          SSL_context = SSL_CTX_new(SSLv23_method());
***************
*** 780,786 ****
      SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2);

      /* setup the allowed cipher list */
!     if (SSL_CTX_set_cipher_list(SSL_context, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH") != 1)
          elog(FATAL, "could not set the cipher list (no valid ciphers available)");

      /*
--- 791,797 ----
      SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2);

      /* setup 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)");

      /*
Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v
retrieving revision 1.521
diff -c -c -r1.521 postmaster.c
*** src/backend/postmaster/postmaster.c    13 Feb 2007 19:18:54 -0000    1.521
--- src/backend/postmaster/postmaster.c    16 Feb 2007 01:26:25 -0000
***************
*** 186,191 ****
--- 186,192 ----

  /* still more option variables */
  bool        EnableSSL = false;
+ char       *SSLCipherSuites;
  bool        SilentMode = false; /* silent mode (-S) */

  int            PreAuthDelay = 0;
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v
retrieving revision 1.374
diff -c -c -r1.374 guc.c
*** src/backend/utils/misc/guc.c    14 Feb 2007 03:08:44 -0000    1.374
--- src/backend/utils/misc/guc.c    16 Feb 2007 01:26:28 -0000
***************
*** 2314,2319 ****
--- 2314,2329 ----
          NULL, assign_temp_tablespaces, NULL
      },

+     {
+         {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+             gettext_noop("Sets the list of allowed SSL ciphers."),
+             NULL,
+             GUC_SUPERUSER_ONLY
+         },
+         &SSLCipherSuites,
+         "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH", NULL, NULL
+     },
+
      /* End-of-list marker */
      {
          {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL
Index: src/backend/utils/misc/postgresql.conf.sample
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.207
diff -c -c -r1.207 postgresql.conf.sample
*** src/backend/utils/misc/postgresql.conf.sample    25 Jan 2007 15:05:15 -0000    1.207
--- src/backend/utils/misc/postgresql.conf.sample    16 Feb 2007 01:26:28 -0000
***************
*** 74,79 ****
--- 74,80 ----

  #authentication_timeout = 1min        # 1s-600s
  #ssl = off                # (change requires restart)
+ #ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH' # List of ciphers to use
  #password_encryption = on
  #db_user_namespace = off

Index: src/include/postmaster/postmaster.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/postmaster/postmaster.h,v
retrieving revision 1.15
diff -c -c -r1.15 postmaster.h
*** src/include/postmaster/postmaster.h    5 Jan 2007 22:19:57 -0000    1.15
--- src/include/postmaster/postmaster.h    16 Feb 2007 01:26:28 -0000
***************
*** 15,20 ****
--- 15,21 ----

  /* GUC options */
  extern bool EnableSSL;
+ extern char *SSLCipherSuites;
  extern bool SilentMode;
  extern int    ReservedBackends;
  extern int    PostPortNumber;
Index: src/interfaces/libpq/fe-secure.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v
retrieving revision 1.92
diff -c -c -r1.92 fe-secure.c
*** src/interfaces/libpq/fe-secure.c    8 Feb 2007 11:10:27 -0000    1.92
--- src/interfaces/libpq/fe-secure.c    16 Feb 2007 01:26:29 -0000
***************
*** 111,116 ****
--- 111,122 ----

  #ifdef USE_SSL
  #include <openssl/ssl.h>
+ #if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
+ #include <openssl/conf.h>
+ #endif
+ #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
+ #include <openssl/engine.h>
+ #endif
  #endif   /* USE_SSL */


***************
*** 606,659 ****
      }
      fclose(fp);

!     /* read the user key */
!     snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
!     if (stat(fnbuf, &buf) == -1)
!     {
!         printfPQExpBuffer(&conn->errorMessage,
!                           libpq_gettext("certificate present, but not private key file \"%s\"\n"),
!                           fnbuf);
!         return 0;
!     }
! #ifndef WIN32
!     if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) ||
!         buf.st_uid != geteuid())
!     {
!         printfPQExpBuffer(&conn->errorMessage,
!             libpq_gettext("private key file \"%s\" has wrong permissions\n"),
!                           fnbuf);
!         return 0;
!     }
! #endif
!     if ((fp = fopen(fnbuf, "r")) == NULL)
!     {
!         printfPQExpBuffer(&conn->errorMessage,
!                libpq_gettext("could not open private key file \"%s\": %s\n"),
!                           fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
!         return 0;
!     }
! #ifndef WIN32
!     if (fstat(fileno(fp), &buf2) == -1 ||
!         buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
      {
!         printfPQExpBuffer(&conn->errorMessage,
!                           libpq_gettext("private key file \"%s\" changed during execution\n"), fnbuf);
!         return 0;
      }
  #endif
-     if (PEM_read_PrivateKey(fp, pkey, NULL, NULL) == NULL)
      {
!         char       *err = SSLerrmessage();

!         printfPQExpBuffer(&conn->errorMessage,
!                libpq_gettext("could not read private key file \"%s\": %s\n"),
!                           fnbuf, err);
!         SSLerrfree(err);
          fclose(fp);
-         return 0;
      }
-     fclose(fp);
-
      /* verify that the cert and key go together */
      if (!X509_check_private_key(*x509, *pkey))
      {
--- 612,710 ----
      }
      fclose(fp);

! #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
!     if (getenv("PGSSLKEY"))
      {
!         /* read the user key from engine */
!         char    *engine_env = getenv("PGSSLKEY");
!         char    *engine_colon = strchr(engine_env, ':');
!         char    *engine_str;
!         ENGINE    *engine_ptr = NULL;
!
!         if (!engine_colon)
!         {
!             printfPQExpBuffer(&conn->errorMessage,
!                 libpq_gettext("invalid value of PGSSLKEY environment variable\n"));
!             return 0;
!         }
!
!         engine_str = malloc(engine_colon - engine_env + 1);
!         strlcpy(engine_str, engine_env, engine_colon - engine_env + 1);
!         if ((engine_ptr = ENGINE_by_id(engine_str)) == NULL)
!         {
!             char      *err = SSLerrmessage();
!
!             printfPQExpBuffer(&conn->errorMessage,
!                 libpq_gettext("could not load SSL engine \"%s\":%s\n"), engine_str, err);
!             free(engine_str);
!             SSLerrfree(err);
!             return 0;
!         }
!         if ((*pkey = ENGINE_load_private_key(engine_ptr,
!                         engine_colon + 1, NULL, NULL)) == NULL)
!         {
!             char      *err = SSLerrmessage();
!
!             printfPQExpBuffer(&conn->errorMessage,
!                 libpq_gettext("could not read private SSL key %s from engine \"%s\": %s\n"),
!                             engine_colon + 1, engine_str, err);
!             SSLerrfree(err);
!             free(engine_str);
!             return 0;
!         }
!         free(engine_str);
      }
+     else
  #endif
      {
!         /* read the user key from file*/
!         snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
!         if (stat(fnbuf, &buf) == -1)
!         {
!             printfPQExpBuffer(&conn->errorMessage,
!                             libpq_gettext("certificate present, but not private key file \"%s\"\n"),
!                             fnbuf);
!             return 0;
!         }
!     #ifndef WIN32
!         if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) ||
!             buf.st_uid != geteuid())
!         {
!             printfPQExpBuffer(&conn->errorMessage,
!                 libpq_gettext("private key file \"%s\" has wrong permissions\n"),
!                             fnbuf);
!             return 0;
!         }
!     #endif
!         if ((fp = fopen(fnbuf, "r")) == NULL)
!         {
!             printfPQExpBuffer(&conn->errorMessage,
!                 libpq_gettext("could not open private key file \"%s\": %s\n"),
!                             fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
!             return 0;
!         }
!     #ifndef WIN32
!         if (fstat(fileno(fp), &buf2) == -1 ||
!             buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
!         {
!             printfPQExpBuffer(&conn->errorMessage,
!                             libpq_gettext("private key file \"%s\" changed during execution\n"), fnbuf);
!             return 0;
!         }
!     #endif
!         if (PEM_read_PrivateKey(fp, pkey, NULL, NULL) == NULL)
!         {
!             char       *err = SSLerrmessage();

!             printfPQExpBuffer(&conn->errorMessage,
!                 libpq_gettext("could not read private key file \"%s\": %s\n"),
!                             fnbuf, err);
!             SSLerrfree(err);
!             fclose(fp);
!             return 0;
!         }
          fclose(fp);
      }
      /* verify that the cert and key go together */
      if (!X509_check_private_key(*x509, *pkey))
      {
***************
*** 737,742 ****
--- 788,796 ----
      {
          if (pq_initssllib)
          {
+ #if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
+             OPENSSL_config(NULL);
+ #endif
              SSL_library_init();
              SSL_load_error_strings();
          }

pgsql-patches by date:

Previous
From: Andrew Dunstan
Date:
Subject: Re: patch adding new regexp functions
Next
From: "FAST PostgreSQL"
Date:
Subject: Re: WIP patch - INSERT-able log statements