diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index e5c73b4e361bf7e0f092dca7a43d13b9ceb0150d..aa5c15f1a01f110118c70513b2b04c5744a84393 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.110 2007/02/08 15:46:03 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.111 2007/02/16 02:59:40 momjian Exp $ --> <chapter Id="runtime-config"> <title>Server Configuration</title> @@ -569,6 +569,20 @@ SET ENABLE_SEQSCAN TO OFF; </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> diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 4c017334e1da2d8aad6d798e7f367fa71506ceeb..dd8ab08fe2bb960789ffa0cf5fe1c434086a5c56 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.228 2007/02/06 03:03:11 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.229 2007/02/16 02:59:40 momjian Exp $ --> <chapter id="libpq"> <title><application>libpq</application> - C Library</title> @@ -4174,6 +4174,18 @@ setting, and is only available if </listitem> <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> @@ -4438,19 +4450,44 @@ ldap://ldap.mycompany.com/dc=mycompany,dc=com?uniqueMember?one?(cn=mydatabase) 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. + 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, diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 4baec332766137a0692266a4b6baae0966f1b348..7bed97a86f3dc3684f88c0c3c1d14356e30b16e7 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.376 2007/02/01 00:28:18 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.377 2007/02/16 02:59:40 momjian Exp $ --> <chapter Id="runtime"> <title>Operating System Environment</title> @@ -1515,6 +1515,25 @@ $ <userinput>kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`</userinput require use of <acronym>SSL</> for some or all connections. </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 @@ -1528,8 +1547,8 @@ $ <userinput>kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`</userinput <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 + 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 diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c index 8a54275d9cdbbe135f138cfe64a7def003684dad..1fb648fb8eda7fe428cb7118f1bfc5687e5c1677 100644 --- a/src/backend/libpq/be-secure.c +++ b/src/backend/libpq/be-secure.c @@ -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 @@ -92,6 +92,10 @@ #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,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)"); /* diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index f7411210a74c0e6dfb8e4eeeb6918a266bd16a53..0a0a677e6efb607d5eabb520776b085ba7caf48c 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -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; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index e66573d7380e950893ce7b4a5f58449b8fb8491d..b5d93d6d64db53f754e8db32ee0187eb1cbc8700 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut <peter_e@gmx.net>. * * 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 diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 2e708b11623aa7bfd1afc608d46f53461f6c0c2b..ca5b2aafb1100401baeb895508af73a276c521a0 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -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 diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h index 26818c173442485b17041436a3d893e29428a2cb..811e65af55ba74dbf4715a5fd2f13d9b7c0e82d1 100644 --- a/src/include/postmaster/postmaster.h +++ b/src/include/postmaster/postmaster.h @@ -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; diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c index 2d387b19d2721ba1d451a7f3bae8f0ebc842f83e..f97d4c5334495c994c80342c19415395520ef13d 100644 --- a/src/interfaces/libpq/fe-secure.c +++ b/src/interfaces/libpq/fe-secure.c @@ -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 ] @@ -111,6 +111,12 @@ #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,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(); }