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  <[EMAIL PROTECTED]>          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();
  		}
---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?

               http://archives.postgresql.org

Reply via email to