1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-13 07:41:39 +03:00

UPDATED PATCH:

Attached are a revised set of SSL patches.  Many of these patches
are motivated by security concerns, it's not just bug fixes.  The key
differences (from stock 7.2.1) are:

*) almost all code that directly uses the OpenSSL library is in two
   new files,

     src/interfaces/libpq/fe-ssl.c
     src/backend/postmaster/be-ssl.c

   in the long run, it would be nice to merge these two files.

*) the legacy code to read and write network data have been
   encapsulated into read_SSL() and write_SSL().  These functions
   should probably be renamed - they handle both SSL and non-SSL
   cases.

   the remaining code should eliminate the problems identified
   earlier, albeit not very cleanly.

*) both front- and back-ends will send a SSL shutdown via the
   new close_SSL() function.  This is necessary for sessions to
   work properly.

   (Sessions are not yet fully supported, but by cleanly closing
   the SSL connection instead of just sending a TCP FIN packet
   other SSL tools will be much happier.)

*) The client certificate and key are now expected in a subdirectory
   of the user's home directory.  Specifically,

	- the directory .postgresql must be owned by the user, and
	  allow no access by 'group' or 'other.'

	- the file .postgresql/postgresql.crt must be a regular file
	  owned by the user.

	- the file .postgresql/postgresql.key must be a regular file
	  owned by the user, and allow no access by 'group' or 'other'.

   At the current time encrypted private keys are not supported.
   There should also be a way to support multiple client certs/keys.

*) the front-end performs minimal validation of the back-end cert.
   Self-signed certs are permitted, but the common name *must*
   match the hostname used by the front-end.  (The cert itself
   should always use a fully qualified domain name (FDQN) in its
   common name field.)

   This means that

	  psql -h eris db

   will fail, but

	  psql -h eris.example.com db

   will succeed.  At the current time this must be an exact match;
   future patches may support any FQDN that resolves to the address
   returned by getpeername(2).

   Another common "problem" is expiring certs.  For now, it may be
   a good idea to use a very-long-lived self-signed cert.

   As a compile-time option, the front-end can specify a file
   containing valid root certificates, but it is not yet required.

*) the back-end performs minimal validation of the client cert.
   It allows self-signed certs.  It checks for expiration.  It
   supports a compile-time option specifying a file containing
   valid root certificates.

*) both front- and back-ends default to TLSv1, not SSLv3/SSLv2.

*) both front- and back-ends support DSA keys.  DSA keys are
   moderately more expensive on startup, but many people consider
   them preferable than RSA keys.  (E.g., SSH2 prefers DSA keys.)

*) if /dev/urandom exists, both client and server will read 16k
   of randomization data from it.

*) the server can read empheral DH parameters from the files

     $DataDir/dh512.pem
     $DataDir/dh1024.pem
     $DataDir/dh2048.pem
     $DataDir/dh4096.pem

   if none are provided, the server will default to hardcoded
   parameter files provided by the OpenSSL project.

Remaining tasks:

*) the select() clauses need to be revisited - the SSL abstraction
   layer may need to absorb more of the current code to avoid rare
   deadlock conditions.  This also touches on a true solution to
   the pg_eof() problem.

*) the SIGPIPE signal handler may need to be revisited.

*) support encrypted private keys.

*) sessions are not yet fully supported.  (SSL sessions can span
   multiple "connections," and allow the client and server to avoid
   costly renegotiations.)

*) makecert - a script that creates back-end certs.

*) pgkeygen - a tool that creates front-end certs.

*) the whole protocol issue, SASL, etc.

 *) certs are fully validated - valid root certs must be available.
    This is a hassle, but it means that you *can* trust the identity
    of the server.

 *) the client library can handle hardcoded root certificates, to
    avoid the need to copy these files.

 *) host name of server cert must resolve to IP address, or be a
    recognized alias.  This is more liberal than the previous
    iteration.

 *) the number of bytes transferred is tracked, and the session
    key is periodically renegotiated.

 *) basic cert generation scripts (mkcert.sh, pgkeygen.sh).  The
    configuration files have reasonable defaults for each type
    of use.

Bear Giles
This commit is contained in:
Bruce Momjian
2002-06-14 04:23:17 +00:00
parent eb43af3210
commit 19570420f5
9 changed files with 996 additions and 211 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.185 2002/06/14 04:09:37 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.186 2002/06/14 04:23:17 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -61,9 +61,12 @@ inet_aton(const char *cp, struct in_addr * inp)
}
#endif
#ifdef USE_SSL
static SSL_CTX *SSL_context = NULL;
extern int secure_initialize(PGconn *);
extern void secure_destroy(void);
extern int secure_open_client(PGconn *);
extern void secure_close(PGconn *);
extern SSL * PQgetssl(PGconn *);
#endif
#define NOTIFYLIST_INITIAL_SIZE 10
@ -186,10 +189,6 @@ static char *conninfo_getval(PQconninfoOption *connOptions,
static void defaultNoticeProcessor(void *arg, const char *message);
static int parseServiceInfo(PQconninfoOption *options,
PQExpBuffer errorMessage);
#ifdef USE_SSL
static const char *SSLerrmessage(void);
#endif
/*
* Connecting to a Database
@ -969,26 +968,8 @@ retry2:
}
if (SSLok == 'S')
{
if (!SSL_context)
if (secure_initialize(conn) == -1 || secure_open_client(conn) == -1)
{
SSL_load_error_strings();
SSL_library_init();
SSL_context = SSL_CTX_new(SSLv23_method());
if (!SSL_context)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not create SSL context: %s\n"),
SSLerrmessage());
goto connect_errReturn;
}
}
if (!(conn->ssl = SSL_new(SSL_context)) ||
!SSL_set_fd(conn->ssl, conn->sock) ||
SSL_connect(conn->ssl) <= 0)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not establish SSL connection: %s\n"),
SSLerrmessage());
goto connect_errReturn;
}
/* SSL connection finished. Continue to send startup packet */
@ -998,6 +979,7 @@ retry2:
/* Received error - probably protocol mismatch */
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "Postmaster reports error, attempting fallback to pre-7.0.\n");
secure_close(conn);
#ifdef WIN32
closesocket(conn->sock);
#else
@ -1039,6 +1021,7 @@ retry2:
connect_errReturn:
if (conn->sock >= 0)
{
secure_close(conn);
#ifdef WIN32
closesocket(conn->sock);
#else
@ -1914,8 +1897,7 @@ freePGconn(PGconn *conn)
return;
pqClearAsyncResult(conn); /* deallocate result and curTuple */
#ifdef USE_SSL
if (conn->ssl)
SSL_free(conn->ssl);
secure_close(conn);
#endif
if (conn->sock >= 0)
{
@ -1992,6 +1974,7 @@ closePGconn(PGconn *conn)
*/
if (conn->sock >= 0)
{
secure_close(conn);
#ifdef WIN32
closesocket(conn->sock);
#else
@ -2641,35 +2624,6 @@ PQconninfoFree(PQconninfoOption *connOptions)
}
#ifdef USE_SSL
/*
* Obtain reason string for last SSL error
*
* Some caution is needed here since ERR_reason_error_string will
* return NULL if it doesn't recognize the error code. We don't
* want to return NULL ever.
*/
static const char *
SSLerrmessage(void)
{
unsigned long errcode;
const char *errreason;
static char errbuf[32];
errcode = ERR_get_error();
if (errcode == 0)
return "No SSL error reported";
errreason = ERR_reason_error_string(errcode);
if (errreason != NULL)
return errreason;
snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode);
return errbuf;
}
#endif /* USE_SSL */
/* =========== accessor functions for PGconn ========= */
char *
PQdb(const PGconn *conn)
@ -2814,16 +2768,6 @@ PQsetClientEncoding(PGconn *conn, const char *encoding)
}
#endif
#ifdef USE_SSL
SSL *
PQgetssl(PGconn *conn)
{
if (!conn)
return NULL;
return conn->ssl;
}
#endif
void
PQtrace(PGconn *conn, FILE *debug_port)
{