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.

diff -rcN ../pgsql-20060830/doc/src/sgml/config.sgml ./doc/src/sgml/config.sgml
*** ../pgsql-20060830/doc/src/sgml/config.sgml  2006-08-30 16:01:12.000000000 
+0400
--- ./doc/src/sgml/config.sgml  2006-09-01 11:13:34.000000000 +0400
***************
*** 555,561 ****
         </para>
        </listitem>
       </varlistentry>
! 
       <varlistentry id="guc-password-encryption" 
xreflabel="password_encryption">
        <term><varname>password_encryption</varname> 
(<type>boolean</type>)</term>
        <indexterm>
--- 555,575 ----
         </para>
        </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 list of <acronym>SSL</> ciphers, which can be used to
!               establish secure connection. See manual page for
!               <command>openssl ciphers</command>
!               command to find list of allowed values and their semantics.
!         </para>       
!        </listitem>
!        </varlistentry>
!        
       <varlistentry id="guc-password-encryption" 
xreflabel="password_encryption">
        <term><varname>password_encryption</varname> 
(<type>boolean</type>)</term>
        <indexterm>
diff -rcN ../pgsql-20060830/doc/src/sgml/libpq.sgml ./doc/src/sgml/libpq.sgml
*** ../pgsql-20060830/doc/src/sgml/libpq.sgml   2006-08-30 16:01:12.000000000 
+0400
--- ./doc/src/sgml/libpq.sgml   2006-09-01 12:30:33.000000000 +0400
***************
*** 3942,3947 ****
--- 3942,3959 ----
  <listitem>
  <para>
  <indexterm>
+ <primary><envar>PGSSLKEY</envar></primary>
+ </indexterm>
+ <envar>PGSSLKEY</envar>
+ specifies hardware token which store secret key of the client
+ certificate, instead of file. Value of this variable should consist of
+ colon separated engine name (engines are loadable modules of
+ <productname>OpenSSL</>) and 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
***************
*** 4139,4158 ****
     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
--- 4151,4194 ----
     for increased security. See <xref linkend="ssl-tcp"> for details
     about the server-side <acronym>SSL</> functionality.
    </para>
!   <para>
!   <application>libpq</application> reads system-wide
!   <productname>OpenSSL</productname> configuration file. By default this
!   file is named <filename>openssl.cnf</filename> and located in the
!   directory, which is reported by command:
!   <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>
     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 secret
!    key is stored on hardware token, 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 environment variable <envar>PGSSLKEY</envar> is set, its value
+       should consist of colon separated engine name and key identifier. In 
this
+       case, <application>libpq</application> would load specified engine,
+       i.e. <productname>OpenSSL</> module which supports special hardware
+       and obtain reference to key with specified identifier. Identifiers
+       are engine-specific. Typically, cryptography hardware tokens do not
+       reveal secret keys to the application. Instead, application delegates
+       all cryptography operations which require 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
diff -rcN ../pgsql-20060830/doc/src/sgml/runtime.sgml 
./doc/src/sgml/runtime.sgml
*** ../pgsql-20060830/doc/src/sgml/runtime.sgml 2006-08-30 16:01:12.000000000 
+0400
--- ./doc/src/sgml/runtime.sgml 2006-09-01 12:30:34.000000000 +0400
***************
*** 1516,1521 ****
--- 1516,1540 ----
    </para>
  
    <para>
+       <productname>OpenSSL</productname> supports wide range of ciphers
+       and authentication algorithms, which strength varies significantly.
+       You can restrict list of ciphers which can be used to connect to
+       your server using  <xref linkend="guc-ssl-ciphers"> parameter.
+   </para>
+   <para>
+   <productname>PostgreSQL</productname>  reads system-wide
+   <productname>OpenSSL</productname> configuration file. By default this
+   file is named <filename>openssl.cnf</filename> and located in the
+   directory, which is reported by command:
+   <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
diff -rcN ../pgsql-20060830/src/backend/libpq/be-secure.c 
./src/backend/libpq/be-secure.c
*** ../pgsql-20060830/src/backend/libpq/be-secure.c     2006-08-30 
16:01:28.000000000 +0400
--- ./src/backend/libpq/be-secure.c     2006-09-01 11:59:16.000000000 +0400
***************
*** 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
  
  /* ------------------------------------------------------------ */
***************
*** 717,722 ****
--- 725,733 ----
  
        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());
***************
*** 778,784 ****
        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)");
  
        /*
--- 789,795 ----
        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)");
  
        /*
diff -rcN ../pgsql-20060830/src/backend/postmaster/postmaster.c 
./src/backend/postmaster/postmaster.c
*** ../pgsql-20060830/src/backend/postmaster/postmaster.c       2006-08-30 
16:01:32.000000000 +0400
--- ./src/backend/postmaster/postmaster.c       2006-09-01 11:16:03.000000000 
+0400
***************
*** 186,191 ****
--- 186,192 ----
  
  /* still more option variables */
  bool          EnableSSL = false;
+ char *                SSLCipherSuites;
  bool          SilentMode = false; /* silent mode (-S) */
  
  int                   PreAuthDelay = 0;
diff -rcN ../pgsql-20060830/src/backend/utils/misc/guc.c 
./src/backend/utils/misc/guc.c
*** ../pgsql-20060830/src/backend/utils/misc/guc.c      2006-08-30 
16:01:36.000000000 +0400
--- ./src/backend/utils/misc/guc.c      2006-09-01 11:17:04.000000000 +0400
***************
*** 2233,2239 ****
                &external_pid_file,
                NULL, assign_canonical_path, NULL
        },
! 
        /* End-of-list marker */
        {
                {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL
--- 2233,2248 ----
                &external_pid_file,
                NULL, assign_canonical_path, NULL
        },
!       {
!               {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
!                       gettext_noop("List of allowed SSL ciphersuites"),
!                       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
diff -rcN ../pgsql-20060830/src/backend/utils/misc/postgresql.conf.sample 
./src/backend/utils/misc/postgresql.conf.sample
*** ../pgsql-20060830/src/backend/utils/misc/postgresql.conf.sample     
2006-08-30 16:01:36.000000000 +0400
--- ./src/backend/utils/misc/postgresql.conf.sample     2006-09-01 
11:17:04.000000000 +0400
***************
*** 71,76 ****
--- 71,77 ----
  
  #authentication_timeout = 60          # 1-600, in seconds
  #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
  
diff -rcN ../pgsql-20060830/src/include/postmaster/postmaster.h 
./src/include/postmaster/postmaster.h
*** ../pgsql-20060830/src/include/postmaster/postmaster.h       2006-08-30 
16:01:42.000000000 +0400
--- ./src/include/postmaster/postmaster.h       2006-09-01 11:17:04.000000000 
+0400
***************
*** 15,20 ****
--- 15,21 ----
  
  /* GUC options */
  extern bool EnableSSL;
+ extern char *SSLCipherSuites;
  extern bool SilentMode;
  extern int    ReservedBackends;
  extern int    PostPortNumber;
diff -rcN ../pgsql-20060830/src/interfaces/libpq/fe-secure.c 
./src/interfaces/libpq/fe-secure.c
*** ../pgsql-20060830/src/interfaces/libpq/fe-secure.c  2006-08-30 
16:01:45.000000000 +0400
--- ./src/interfaces/libpq/fe-secure.c  2006-08-30 21:03:28.000000000 +0400
***************
*** 115,120 ****
--- 115,126 ----
  
  #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 */
  
  
***************
*** 611,664 ****
        }
        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, cb, 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))
        {
--- 617,707 ----
        }
        fclose(fp);
  
! #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
!       if (getenv("PGSSLKEY")) {
!               /* read the user key from engine */
!               ENGINE *e = NULL;
!               char *engine_id;
!               char *l = getenv("PGSSLKEY");
!               char *p = strchr(l,':');
!               if (!p) {
!                       
printfPQExpBuffer(&conn->errorMessage,libpq_gettext("Invalid value of PGSSLKEY 
environment variable\n"));
!                       return 0;
!               }
!               engine_id = malloc(p-l+1);
!               strncpy(engine_id,l,p-l);
!               engine_id[p-l]=0;
!               e = ENGINE_by_id(engine_id);
!               if (!e) {
!                       char      *err = SSLerrmessage();
!                       
printfPQExpBuffer(&conn->errorMessage,libpq_gettext("Couldn't load engine 
\"%s\":%s\n"),engine_id,err);
!                       free(engine_id);
!                       SSLerrfree(err);
!                       return 0;
!               }       
!               *pkey = ENGINE_load_private_key(e,p+1,NULL,NULL);       
!               if (!*pkey) {
!                       char      *err = SSLerrmessage();
!                       printfPQExpBuffer(&conn->errorMessage,
!                               libpq_gettext("could not read prevate key %s 
from engine \"%s\": %s\n"),
!                                                       p+1,engine_id, err);
!                       SSLerrfree(err);
!                       free(engine_id);
!                       return 0;
!               }               
!               free(engine_id);
!       } else {        
! #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, cb, 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))
        {
***************
*** 742,747 ****
--- 785,793 ----
        {
                if (pq_initssllib)
                {
+ #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) 
+                       OPENSSL_config(NULL);
+ #endif                        
                        SSL_library_init();
                        SSL_load_error_strings();
                }
---------------------------(end of broadcast)---------------------------
TIP 5: don't forget to increase your free space map settings

Reply via email to