1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-12 15:23:02 +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

@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.278 2002/06/14 04:09:36 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.279 2002/06/14 04:23:17 momjian Exp $
*
* NOTES
*
@@ -165,10 +165,6 @@ static int ServerSock_INET = INVALID_SOCK; /* stream socket server */
static int ServerSock_UNIX = INVALID_SOCK; /* stream socket server */
#endif
#ifdef USE_SSL
static SSL_CTX *SSL_context = NULL; /* Global SSL context */
#endif
/*
* Set by the -o option
*/
@@ -245,7 +241,7 @@ static void CleanupProc(int pid, int exitstatus);
static void LogChildExit(int lev, const char *procname,
int pid, int exitstatus);
static int DoBackend(Port *port);
static void ExitPostmaster(int status);
void ExitPostmaster(int status);
static void usage(const char *);
static int ServerLoop(void);
static int BackendStartup(Port *port);
@@ -264,7 +260,7 @@ static void SignalChildren(int signal);
static int CountChildren(void);
static bool CreateOptsFile(int argc, char *argv[]);
static pid_t SSDataBase(int xlop);
static void
void
postmaster_error(const char *fmt,...)
/* This lets gcc check the format string for consistency. */
__attribute__((format(printf, 1, 2)));
@@ -274,9 +270,11 @@ __attribute__((format(printf, 1, 2)));
#define ShutdownDataBase() SSDataBase(BS_XLOG_SHUTDOWN)
#ifdef USE_SSL
static void InitSSL(void);
static const char *SSLerrmessage(void);
#endif
extern int secure_initialize(void);
extern void secure_destroy(void);
extern int secure_open_server(Port *);
extern void secure_close(Port *);
#endif /* USE_SSL */
static void
@@ -609,7 +607,7 @@ PostmasterMain(int argc, char *argv[])
ExitPostmaster(1);
}
if (EnableSSL)
InitSSL();
secure_initialize();
#endif
/*
@@ -1113,17 +1111,8 @@ ProcessStartupPacket(Port *port, bool SSLdone)
}
#ifdef USE_SSL
if (SSLok == 'S')
{
if (!(port->ssl = SSL_new(SSL_context)) ||
!SSL_set_fd(port->ssl, port->sock) ||
SSL_accept(port->ssl) <= 0)
{
elog(LOG, "failed to initialize SSL connection: %s (%m)",
SSLerrmessage());
if (SSLok == 'S' && secure_open_server(port) == -1)
return STATUS_ERROR;
}
}
#endif
/* regular startup packet, cancel, etc packet should follow... */
/* but not another SSL negotiation request */
@@ -1322,8 +1311,7 @@ static void
ConnFree(Port *conn)
{
#ifdef USE_SSL
if (conn->ssl)
SSL_free(conn->ssl);
secure_close(conn);
#endif
free(conn);
}
@@ -2246,7 +2234,7 @@ DoBackend(Port *port)
*
* Do NOT call exit() directly --- always go through here!
*/
static void
void
ExitPostmaster(int status)
{
/* should cleanup shared memory and kill all backends */
@@ -2424,73 +2412,6 @@ CountChildren(void)
return cnt;
}
#ifdef USE_SSL
/*
* Initialize SSL library and structures
*/
static void
InitSSL(void)
{
char fnbuf[2048];
SSL_load_error_strings();
SSL_library_init();
SSL_context = SSL_CTX_new(SSLv23_method());
if (!SSL_context)
{
postmaster_error("failed to create SSL context: %s",
SSLerrmessage());
ExitPostmaster(1);
}
snprintf(fnbuf, sizeof(fnbuf), "%s/server.crt", DataDir);
if (!SSL_CTX_use_certificate_file(SSL_context, fnbuf, SSL_FILETYPE_PEM))
{
postmaster_error("failed to load server certificate (%s): %s",
fnbuf, SSLerrmessage());
ExitPostmaster(1);
}
snprintf(fnbuf, sizeof(fnbuf), "%s/server.key", DataDir);
if (!SSL_CTX_use_PrivateKey_file(SSL_context, fnbuf, SSL_FILETYPE_PEM))
{
postmaster_error("failed to load private key file (%s): %s",
fnbuf, SSLerrmessage());
ExitPostmaster(1);
}
if (!SSL_CTX_check_private_key(SSL_context))
{
postmaster_error("check of private key failed: %s",
SSLerrmessage());
ExitPostmaster(1);
}
}
/*
* 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 */
/*
* Fire off a subprocess for startup/shutdown/checkpoint.
*
@@ -2693,7 +2614,7 @@ CreateOptsFile(int argc, char *argv[])
* This should be used only for reporting "interactive" errors (ie, errors
* during startup. Once the postmaster is launched, use elog.
*/
static void
void
postmaster_error(const char *fmt,...)
{
va_list ap;