Break out OpenSSL-specific code to separate files.

This refactoring is in preparation for adding support for other SSL
implementations, with no user-visible effects. There are now two #defines,
USE_OPENSSL which is defined when building with OpenSSL, and USE_SSL which
is defined when building with any SSL implementation. Currently, OpenSSL is
the only implementation so the two #defines go together, but USE_SSL is
supposed to be used for implementation-independent code.

The libpq SSL code is changed to use a custom BIO, which does all the raw
I/O, like we've been doing in the backend for a long time. That makes it
possible to use MSG_NOSIGNAL to block SIGPIPE when using SSL, which avoids
a couple of syscall for each send(). Probably doesn't make much performance
difference in practice - the SSL encryption is expensive enough to mask the
effect - but it was a natural result of this refactoring.

Based on a patch by Martijn van Oosterhout from 2006. Briefly reviewed by
Alvaro Herrera, Andreas Karlsson, Jeff Janes.
This commit is contained in:
Heikki Linnakangas 2014-08-11 11:54:19 +03:00
parent 6aa61580e0
commit 680513ab79
26 changed files with 2782 additions and 2428 deletions

2
configure vendored
View File

@ -5492,7 +5492,7 @@ if test "${with_openssl+set}" = set; then :
case $withval in
yes)
$as_echo "#define USE_SSL 1" >>confdefs.h
$as_echo "#define USE_OPENSSL 1" >>confdefs.h
;;
no)

View File

@ -657,7 +657,7 @@ AC_MSG_RESULT([$with_bonjour])
#
AC_MSG_CHECKING([whether to build with OpenSSL support])
PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
[AC_DEFINE([USE_SSL], 1, [Define to build with (Open)SSL support. (--with-openssl)])])
[AC_DEFINE([USE_OPENSSL], 1, [Define to build with OpenSSL support. (--with-openssl)])])
AC_MSG_RESULT([$with_openssl])
AC_SUBST(with_openssl)

View File

@ -17,4 +17,8 @@ include $(top_builddir)/src/Makefile.global
OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \
pqformat.o pqsignal.o
ifeq ($(with_openssl),yes)
OBJS += be-secure-openssl.o
endif
include $(top_srcdir)/src/backend/common.mk

View File

@ -161,7 +161,7 @@ static int pg_SSPI_recvauth(Port *port);
* RADIUS Authentication
*----------------------------------------------------------------
*/
#ifdef USE_SSL
#ifdef USE_OPENSSL
#include <openssl/rand.h>
#endif
static int CheckRADIUSAuth(Port *port);
@ -330,7 +330,7 @@ ClientAuthentication(Port *port)
* already if it didn't verify ok.
*/
#ifdef USE_SSL
if (!port->peer)
if (!port->peer_cert_valid)
{
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@ -378,7 +378,7 @@ ClientAuthentication(Port *port)
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\", %s",
hostinfo, port->user_name,
port->ssl ? _("SSL on") : _("SSL off"))));
port->ssl_in_use ? _("SSL on") : _("SSL off"))));
#else
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@ -394,7 +394,7 @@ ClientAuthentication(Port *port)
errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\", %s",
hostinfo, port->user_name,
port->database_name,
port->ssl ? _("SSL on") : _("SSL off"))));
port->ssl_in_use ? _("SSL on") : _("SSL off"))));
#else
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@ -452,7 +452,7 @@ ClientAuthentication(Port *port)
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\", %s",
hostinfo, port->user_name,
port->ssl ? _("SSL on") : _("SSL off")),
port->ssl_in_use ? _("SSL on") : _("SSL off")),
HOSTNAME_LOOKUP_DETAIL(port)));
#else
ereport(FATAL,
@ -470,7 +470,7 @@ ClientAuthentication(Port *port)
errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
hostinfo, port->user_name,
port->database_name,
port->ssl ? _("SSL on") : _("SSL off")),
port->ssl_in_use ? _("SSL on") : _("SSL off")),
HOSTNAME_LOOKUP_DETAIL(port)));
#else
ereport(FATAL,
@ -2315,7 +2315,7 @@ CheckRADIUSAuth(Port *port)
/* Construct RADIUS packet */
packet->code = RADIUS_ACCESS_REQUEST;
packet->length = RADIUS_HEADER_LENGTH;
#ifdef USE_SSL
#ifdef USE_OPENSSL
if (RAND_bytes(packet->vector, RADIUS_VECTOR_LENGTH) != 1)
{
ereport(LOG,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1685,7 +1685,7 @@ check_hba(hbaPort *port)
/* Check SSL state */
#ifdef USE_SSL
if (port->ssl)
if (port->ssl_in_use)
{
/* Connection is SSL, match both "host" and "hostssl" */
if (hba->conntype == ctHostNoSSL)

View File

@ -17,7 +17,7 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#ifdef USE_SSL
#ifdef USE_OPENSSL
#include <openssl/rand.h>
#endif
@ -110,7 +110,7 @@ fork_process(void)
/*
* Make sure processes do not share OpenSSL randomness state.
*/
#ifdef USE_SSL
#ifdef USE_OPENSSL
RAND_cleanup();
#endif
}

View File

@ -231,8 +231,8 @@ PerformAuthentication(Port *port)
{
if (am_walsender)
{
#ifdef USE_SSL
if (port->ssl)
#ifdef USE_OPENSSL
if (port->ssl_in_use)
ereport(LOG,
(errmsg("replication connection authorized: user=%s SSL enabled (protocol=%s, cipher=%s, compression=%s)",
port->user_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl),
@ -245,8 +245,8 @@ PerformAuthentication(Port *port)
}
else
{
#ifdef USE_SSL
if (port->ssl)
#ifdef USE_OPENSSL
if (port->ssl_in_use)
ereport(LOG,
(errmsg("connection authorized: user=%s database=%s SSL enabled (protocol=%s, cipher=%s, compression=%s)",
port->user_name, port->database_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl),

View File

@ -125,9 +125,6 @@ extern char *default_tablespace;
extern char *temp_tablespaces;
extern bool ignore_checksum_failure;
extern bool synchronize_seqscans;
extern char *SSLCipherSuites;
extern char *SSLECDHCurve;
extern bool SSLPreferServerCiphers;
#ifdef TRACE_SORT
extern bool trace_sort;

View File

@ -30,7 +30,7 @@
#include <sys/types.h> /* for umask() */
#include <sys/stat.h> /* for stat() */
#endif
#ifdef USE_SSL
#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#endif
@ -1791,7 +1791,7 @@ connection_warnings(bool in_startup)
static void
printSSLInfo(void)
{
#ifdef USE_SSL
#ifdef USE_OPENSSL
int sslbits = -1;
SSL *ssl;

View File

@ -21,7 +21,7 @@
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef USE_SSL
#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
@ -184,17 +184,33 @@ typedef struct Port
#endif
/*
* SSL structures (keep these last so that USE_SSL doesn't affect
* locations of other fields)
* SSL structures (keep these last so that the locations of other fields
* are the same whether or not you build with SSL)
*/
#ifdef USE_SSL
bool ssl_in_use;
char *peer_cn;
bool peer_cert_valid;
#endif
#ifdef USE_OPENSSL
SSL *ssl;
X509 *peer;
char *peer_cn;
unsigned long count;
#endif
} Port;
#ifdef USE_SSL
/*
* These functions are implemented by the glue code specific to each
* SSL implementation (e.g. be-secure-openssl.c)
*/
extern void be_tls_init(void);
extern int be_tls_open_server(Port *port);
extern void be_tls_close(Port *port);
extern ssize_t be_tls_read(Port *port, void *ptr, size_t len);
extern ssize_t be_tls_write(Port *port, void *ptr, size_t len);
#endif
extern ProtocolVersion FrontendProtocol;

View File

@ -82,5 +82,14 @@ extern int secure_open_server(Port *port);
extern void secure_close(Port *port);
extern ssize_t secure_read(Port *port, void *ptr, size_t len);
extern ssize_t secure_write(Port *port, void *ptr, size_t len);
extern ssize_t secure_raw_read(Port *port, void *ptr, size_t len);
extern ssize_t secure_raw_write(Port *port, const void *ptr, size_t len);
extern bool ssl_loaded_verify_locations;
/* GUCs */
extern char *SSLCipherSuites;
extern char *SSLECDHCurve;
extern bool SSLPreferServerCiphers;
#endif /* LIBPQ_H */

View File

@ -778,15 +778,15 @@
/* Define to select named POSIX semaphores. */
#undef USE_NAMED_POSIX_SEMAPHORES
/* Define to build with OpenSSL support. (--with-openssl) */
#undef USE_OPENSSL
/* Define to 1 to build with PAM support. (--with-pam) */
#undef USE_PAM
/* Use replacement snprintf() functions. */
#undef USE_REPL_SNPRINTF
/* Define to build with (Open)SSL support. (--with-openssl) */
#undef USE_SSL
/* Define to select SysV-style semaphores. */
#undef USE_SYSV_SEMAPHORES

View File

@ -628,15 +628,15 @@
/* Define to select named POSIX semaphores. */
/* #undef USE_NAMED_POSIX_SEMAPHORES */
/* Define to build with OpenSSL support. (--with-openssl) */
/* #undef USE_OPENSSL */
/* Define to 1 to build with PAM support. (--with-pam) */
/* #undef USE_PAM */
/* Use replacement snprintf() functions. */
#define USE_REPL_SNPRINTF 1
/* Define to build with (Open)SSL support. (--with-openssl) */
/* #undef USE_SSL */
/* Define to select SysV-style semaphores. */
/* #undef USE_SYSV_SEMAPHORES */

View File

@ -144,6 +144,15 @@
#define USE_PREFETCH
#endif
/*
* USE_SSL code should be compiled only when compiling with an SSL
* implementation. (Currently, only OpenSSL is supported, but we might add
* more implementations in the future.)
*/
#ifdef USE_OPENSSL
#define USE_SSL
#endif
/*
* This is the default directory in which AF_UNIX socket files are
* placed. Caution: changing this risks breaking your existing client

View File

@ -44,6 +44,10 @@ OBJS += ip.o md5.o
# utils/mb
OBJS += encnames.o wchar.o
ifeq ($(with_openssl),yes)
OBJS += fe-secure-openssl.o
endif
ifeq ($(PORTNAME), cygwin)
override shlib = cyg$(NAME)$(DLSUFFIX)
endif

View File

@ -1961,7 +1961,7 @@ keep_going: /* We will come back to here until there is
conn->allow_ssl_try = false;
}
if (conn->allow_ssl_try && !conn->wait_ssl_try &&
conn->ssl == NULL)
!conn->ssl_in_use)
{
ProtocolVersion pv;
@ -2040,7 +2040,7 @@ keep_going: /* We will come back to here until there is
* On first time through, get the postmaster's response to our
* SSL negotiation packet.
*/
if (conn->ssl == NULL)
if (!conn->ssl_in_use)
{
/*
* We use pqReadData here since it has the logic to
@ -2310,7 +2310,7 @@ keep_going: /* We will come back to here until there is
* connection already, then retry with an SSL connection
*/
if (conn->sslmode[0] == 'a' /* "allow" */
&& conn->ssl == NULL
&& !conn->ssl_in_use
&& conn->allow_ssl_try
&& conn->wait_ssl_try)
{
@ -2709,6 +2709,7 @@ makeEmptyPGconn(void)
#ifdef USE_SSL
conn->allow_ssl_try = true;
conn->wait_ssl_try = false;
conn->ssl_in_use = false;
#endif
/*

View File

@ -751,7 +751,7 @@ retry3:
*/
#ifdef USE_SSL
if (conn->ssl)
if (conn->ssl_in_use)
return 0;
#endif
@ -1051,7 +1051,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
return -1;
}
#ifdef USE_SSL
#ifdef USE_OPENSSL
/* Check for SSL library buffering read bytes */
if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
{

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -73,14 +73,14 @@ typedef struct
#endif
#endif /* ENABLE_SSPI */
#ifdef USE_SSL
#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
#define USE_SSL_ENGINE
#endif
#endif /* USE_SSL */
#endif /* USE_OPENSSL */
/*
* POSTGRES backend dependent Constants.
@ -427,6 +427,8 @@ struct pg_conn
bool allow_ssl_try; /* Allowed to try SSL negotiation */
bool wait_ssl_try; /* Delay SSL negotiation until after
* attempting normal connection */
bool ssl_in_use;
#ifdef USE_OPENSSL
SSL *ssl; /* SSL status, if have SSL connection */
X509 *peer; /* X509 cert of server */
#ifdef USE_SSL_ENGINE
@ -435,6 +437,7 @@ struct pg_conn
void *engine; /* dummy field to keep struct the same if
* OpenSSL version changes */
#endif
#endif /* USE_OPENSSL */
#endif /* USE_SSL */
#ifdef ENABLE_GSS
@ -482,6 +485,24 @@ struct pg_cancel
*/
extern char *const pgresStatus[];
#ifdef USE_SSL
#ifndef WIN32
#define USER_CERT_FILE ".postgresql/postgresql.crt"
#define USER_KEY_FILE ".postgresql/postgresql.key"
#define ROOT_CERT_FILE ".postgresql/root.crt"
#define ROOT_CRL_FILE ".postgresql/root.crl"
#else
/* On Windows, the "home" directory is already PostgreSQL-specific */
#define USER_CERT_FILE "postgresql.crt"
#define USER_KEY_FILE "postgresql.key"
#define ROOT_CERT_FILE "root.crt"
#define ROOT_CRL_FILE "root.crl"
#endif
#endif /* USE_SSL */
/* ----------------
* Internal functions of libpq
* Functions declared here need to be visible across files of libpq,
@ -603,6 +624,8 @@ extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
extern void pqsecure_close(PGconn *);
extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
extern int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
@ -610,6 +633,16 @@ extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
bool got_epipe);
#endif
/*
* The SSL implementatation provides these functions (fe-secure-openssl.c)
*/
extern void pgtls_init_library(bool do_ssl, int do_crypto);
extern int pgtls_init(PGconn *conn);
extern PostgresPollingStatusType pgtls_open_client(PGconn *conn);
extern void pgtls_close(PGconn *conn);
extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);
extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
/*
* this is so that we can check if a connection is non-blocking internally
* without the overhead of a function call

View File

@ -2,7 +2,7 @@
# Will build a static library libpq(d).lib
# and a dynamic library libpq(d).dll with import library libpq(d)dll.lib
# USE_SSL=1 will compile with OpenSSL
# USE_OPENSSL=1 will compile with OpenSSL
# USE_KFW=1 will compile with kfw(kerberos for Windows)
# DEBUG=1 compiles with debugging symbols
# ENABLE_THREAD_SAFETY=1 compiles with threading enabled
@ -124,6 +124,9 @@ CLEAN :
-@erase "$(OUTDIR)\$(OUTFILENAME).dll.manifest"
-@erase "$(OUTDIR)\*.idb"
-@erase pg_config_paths.h"
!IFDEF USE_OPENSSL
-@erase "$(INTDIR)\fe-secure-openssl.obj"
!ENDIF
LIB32=link.exe -lib
@ -164,6 +167,9 @@ LIB32_OBJS= \
"$(INTDIR)\win32error.obj" \
"$(INTDIR)\win32setlocale.obj" \
"$(INTDIR)\pthread-win32.obj"
!IFDEF USE_OPENSSL
LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-openssl.obj"
!ENDIF
config: ..\..\include\pg_config.h ..\..\include\pg_config_ext.h pg_config_paths.h ..\..\include\pg_config_os.h
@ -189,8 +195,8 @@ CPP_PROJ=/nologo /W3 /EHsc $(OPT) /I "..\..\include" /I "..\..\include\port\win3
/Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c \
/D "_CRT_SECURE_NO_DEPRECATE" $(ADD_DEFINES)
!IFDEF USE_SSL
CPP_PROJ=$(CPP_PROJ) /D USE_SSL
!IFDEF USE_OPENSSL
CPP_PROJ=$(CPP_PROJ) /D USE_OPENSSL
SSL_LIBS=ssleay32.lib libeay32.lib gdi32.lib
!ENDIF

View File

@ -117,6 +117,12 @@ sub mkvcbuild
$postgres->AddLibrary('ws2_32.lib');
$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
$postgres->FullExportDLL('postgres.lib');
# The OBJS scraper doesn't know about ifdefs, so remove be-secure-openssl.c
# if building without OpenSSL
if (!$solution->{options}->{openssl})
{
$postgres->RemoveFile('src\backend\libpq\be-secure-openssl.c');
}
my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
'src\backend\snowball');
@ -276,6 +282,12 @@ sub mkvcbuild
$libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c',
'src\interfaces\libpq\libpq.rc');
$libpq->AddReference($libpgport);
# The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
# if building without OpenSSL
if (!$solution->{options}->{openssl})
{
$libpq->RemoveFile('src\interfaces\libpq\fe-secure-openssl.c');
}
my $libpqwalreceiver =
$solution->AddProject('libpqwalreceiver', 'dll', '',

View File

@ -182,7 +182,7 @@ sub GenerateFiles
if ($self->{options}->{integer_datetimes});
print O "#define USE_LDAP 1\n" if ($self->{options}->{ldap});
print O "#define HAVE_LIBZ 1\n" if ($self->{options}->{zlib});
print O "#define USE_SSL 1\n" if ($self->{options}->{openssl});
print O "#define USE_OPENSSL 1\n" if ($self->{options}->{openssl});
print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
print O "#define BLCKSZ ", 1024 * $self->{options}->{blocksize}, "\n";
@ -628,7 +628,7 @@ sub GetFakeConfigure
$cfg .= ' --with-ldap' if ($self->{options}->{ldap});
$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
$cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
$cfg .= ' --with-openssl' if ($self->{options}->{ssl});
$cfg .= ' --with-openssl' if ($self->{options}->{openssl});
$cfg .= ' --with-ossp-uuid' if ($self->{options}->{uuid});
$cfg .= ' --with-libxml' if ($self->{options}->{xml});
$cfg .= ' --with-libxslt' if ($self->{options}->{xslt});

View File

@ -16,7 +16,7 @@ our $config = {
tcl => undef, # --with-tls=<path>
perl => undef, # --with-perl
python => undef, # --with-python=<path>
openssl => undef, # --with-ssl=<path>
openssl => undef, # --with-openssl=<path>
uuid => undef, # --with-ossp-uuid
xml => undef, # --with-libxml=<path>
xslt => undef, # --with-libxslt=<path>