SSL improvements:
authorBruce Momjian
Fri, 16 Feb 2007 02:59:41 +0000 (02:59 +0000)
committerBruce Momjian
Fri, 16 Feb 2007 02:59:41 +0000 (02:59 +0000)
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

Victor B. Wagner

doc/src/sgml/config.sgml
doc/src/sgml/libpq.sgml
doc/src/sgml/runtime.sgml
src/backend/libpq/be-secure.c
src/backend/postmaster/postmaster.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/postmaster/postmaster.h
src/interfaces/libpq/fe-secure.c

index e5c73b4e361bf7e0f092dca7a43d13b9ceb0150d..aa5c15f1a01f110118c70513b2b04c5744a84393 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
   Server Configuration
@@ -569,6 +569,20 @@ SET ENABLE_SEQSCAN TO OFF;
       
      
 
+     
+      ssl_ciphers> (string)
+      
+       ssl_ciphers configuration parameter
+      
+      
+       
+        Specifies a list of SSL ciphers which can be used to
+        establish secure connections. See the openssl
+        manual page for a list of supported ciphers.
+       
+      
+     
+
      
       password_encryption (boolean)
       
index 4c017334e1da2d8aad6d798e7f367fa71506ceeb..dd8ab08fe2bb960789ffa0cf5fe1c434086a5c56 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
  
   <application>libpq</application> - C Library
@@ -4174,6 +4174,18 @@ setting, and is only available if
 
 
 
+
+PGSSLKEY
+
+PGSSLKEY
+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 OpenSSL
+loadable modules) and an engine-specific key identifier.
+
+
+
+
 
  PGKRBSRVNAME
 
@@ -4438,19 +4450,44 @@ ldap://ldap.mycompany.com/dc=mycompany,dc=com?uniqueMember?one?(cn=mydatabase)
    for increased security. See  for details
    about the server-side SSL functionality.
   
-
+  
+  libpq reads the system-wide
+  OpenSSL configuration file. By default, this
+  file is named openssl.cnf and is located in the
+  directory reported by openssl:
+  
+  openssl version -d
+  
+  The default can be overriden by setting environment variable
+  OPENSSL_CONF to the name of the desired configuration
+  file.
+  
   
    If the server demands a client certificate, 
    libpq
    will send the certificate stored in file
    ~/.postgresql/postgresql.crt within the user's home directory.
    A matching private key file ~/.postgresql/postgresql.key
-   must also be present, and must not be world-readable.
+   must also be present, and must not be world-readable, unless the secret
+   key is stored in a hardware token, as specified by
+   PGSSLKEY.
    (On Microsoft Windows these files are named
    %APPDATA%\postgresql\postgresql.crt and
    %APPDATA%\postgresql\postgresql.key.)
   
 
+  
+   If the environment variable PGSSLKEY is set, its value
+   should consist of a colon-separated engine name and key identifier. In
+   this case, libpq will load the specified
+   engine, i.e. the 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.
+    
+   
   
    If the file ~/.postgresql/root.crt is present in the user's
    home directory,
index 4baec332766137a0692266a4b6baae0966f1b348..7bed97a86f3dc3684f88c0c3c1d14356e30b16e7 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
  Operating System Environment
@@ -1515,6 +1515,25 @@ $ kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`
    require use of SSL for some or all connections.
   
 
+  
+   OpenSSL 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  parameter.
+  
+
+  
+   PostgreSQL reads a system-wide
+   OpenSSL configuration file. By default this
+   file is named openssl.cnf and is located in the
+   directory reported by openssl:
+   
+   openssl version -d
+   
+   This default can be overriden by setting environment variable
+   OPENSSL_CONF to the name of desired configuration file.
+  
+
   
    For details on how to create your server private key and certificate,
    refer to the OpenSSL documentation. A
@@ -1528,8 +1547,8 @@ $ kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`
 
 openssl req -new -text -out server.req
 
-   Fill out the information that <command>openssl asks for. Make sure
-   that you enter the local host name as Common Name; the challenge
+   Fill out the information that <application>openssl asks for. Make sure
+   you enter the local host name as 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 8a54275d9cdbbe135f138cfe64a7def003684dad..1fb648fb8eda7fe428cb7118f1bfc5687e5c1677 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.77 2007/02/07 00:52:35 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.78 2007/02/16 02:59:40 momjian Exp $
  *
  *   Since the server static private key ($DataDir/server.key)
  *   will normally be stored unencrypted so that the database
 #ifdef USE_SSL
 #include 
 #include 
+#if SSLEAY_VERSION_NUMBER >= 0x0907000L
+#include 
+#endif
+
 #endif
 
 #include "libpq/libpq.h"
@@ -125,6 +129,10 @@ static const char *SSLerrmessage(void);
 #define RENEGOTIATION_LIMIT (512 * 1024 * 1024)
 
 static SSL_CTX *SSL_context = NULL;
+
+/* GUC variable controlling SSL cipher list*/
+extern char *SSLCipherSuites;
+
 #endif
 
 /* ------------------------------------------------------------ */
@@ -719,6 +727,9 @@ initialize_SSL(void)
 
    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,7 +791,7 @@ initialize_SSL(void)
    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)
+   if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
        elog(FATAL, "could not set the cipher list (no valid ciphers available)");
 
    /*
index f7411210a74c0e6dfb8e4eeeb6918a266bd16a53..0a0a677e6efb607d5eabb520776b085ba7caf48c 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.523 2007/02/16 02:10:07 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.524 2007/02/16 02:59:41 momjian Exp $
  *
  * NOTES
  *
@@ -187,6 +187,7 @@ static int  SendStop = false;
 
 /* still more option variables */
 bool       EnableSSL = false;
+char      *SSLCipherSuites;
 bool       SilentMode = false; /* silent mode (-S) */
 
 int            PreAuthDelay = 0;
index e66573d7380e950893ce7b4a5f58449b8fb8491d..b5d93d6d64db53f754e8db32ee0187eb1cbc8700 100644 (file)
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut .
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.374 2007/02/14 03:08:44 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.375 2007/02/16 02:59:41 momjian Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -2314,6 +2314,16 @@ static struct config_string ConfigureNamesString[] =
        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 2e708b11623aa7bfd1afc608d46f53461f6c0c2b..ca5b2aafb1100401baeb895508af73a276c521a0 100644 (file)
@@ -74,6 +74,7 @@
 
 #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 26818c173442485b17041436a3d893e29428a2cb..811e65af55ba74dbf4715a5fd2f13d9b7c0e82d1 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/postmaster/postmaster.h,v 1.15 2007/01/05 22:19:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/postmaster/postmaster.h,v 1.16 2007/02/16 02:59:41 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,7 @@
 
 /* GUC options */
 extern bool EnableSSL;
+extern char *SSLCipherSuites;
 extern bool SilentMode;
 extern int ReservedBackends;
 extern int PostPortNumber;
index 2d387b19d2721ba1d451a7f3bae8f0ebc842f83e..f97d4c5334495c994c80342c19415395520ef13d 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.92 2007/02/08 11:10:27 petere Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.93 2007/02/16 02:59:41 momjian Exp $
  *
  * NOTES
  *   [ Most of these notes are wrong/obsolete, but perhaps not all ]
 
 #ifdef USE_SSL
 #include 
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
+#include  
+#endif
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
+#include 
+#endif
 #endif   /* USE_SSL */
 
 
@@ -606,54 +612,99 @@ client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
    }
    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)
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
+   if (getenv("PGSSLKEY"))
    {
-       printfPQExpBuffer(&conn->errorMessage,
-                         libpq_gettext("private key file \"%s\" changed during execution\n"), fnbuf);
-       return 0;
+       /* 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
-   if (PEM_read_PrivateKey(fp, pkey, NULL, NULL) == NULL)
    {
-       char       *err = SSLerrmessage();
+       /* 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);
+           printfPQExpBuffer(&conn->errorMessage,
+               libpq_gettext("could not read private key file \"%s\": %s\n"),
+                           fnbuf, err);
+           SSLerrfree(err);
+           fclose(fp);
+           return 0;
+       }
        fclose(fp);
-       return 0;
    }
-   fclose(fp);
-
    /* verify that the cert and key go together */
    if (!X509_check_private_key(*x509, *pkey))
    {
@@ -737,6 +788,9 @@ init_ssl_system(PGconn *conn)
    {
        if (pq_initssllib)
        {
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) 
+           OPENSSL_config(NULL);
+#endif         
            SSL_library_init();
            SSL_load_error_strings();
        }