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();
}