mirror of
https://github.com/postgres/postgres.git
synced 2025-11-26 23:43:30 +03:00
Leave OpenSSL's default passphrase collection callback in place during
the first call of secure_initialize() in server startup. Although that
doesn't work terribly well in daemon contexts, some people feel we should
not break it for anyone who was successfully using it before. We still
block passphrase demands during SIGHUP, meaning that you can't adjust SSL
configuration on-the-fly if you used a passphrase, but this is no worse
than what it was before commit de41869b6. And we block passphrase demands
during EXEC_BACKEND reloads; that behavior wasn't useful either, but at
least now it's documented.
Tweak some related log messages for more readability, and avoid issuing
essentially duplicate messages about reload failure caused by a passphrase.
Discussion: https://postgr.es/m/29982.1483412575@sss.pgh.pa.us
316 lines
6.8 KiB
C
316 lines
6.8 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* be-secure.c
|
|
* functions related to setting up a secure connection to the frontend.
|
|
* Secure connections are expected to provide confidentiality,
|
|
* message integrity and endpoint authentication.
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/libpq/be-secure.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#ifdef HAVE_NETINET_TCP_H
|
|
#include <netinet/tcp.h>
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include "libpq/libpq.h"
|
|
#include "miscadmin.h"
|
|
#include "pgstat.h"
|
|
#include "tcop/tcopprot.h"
|
|
#include "utils/memutils.h"
|
|
#include "storage/ipc.h"
|
|
#include "storage/proc.h"
|
|
|
|
|
|
char *ssl_cert_file;
|
|
char *ssl_key_file;
|
|
char *ssl_ca_file;
|
|
char *ssl_crl_file;
|
|
|
|
#ifdef USE_SSL
|
|
bool ssl_loaded_verify_locations = false;
|
|
#endif
|
|
|
|
/* GUC variable controlling SSL cipher list */
|
|
char *SSLCipherSuites = NULL;
|
|
|
|
/* GUC variable for default ECHD curve. */
|
|
char *SSLECDHCurve;
|
|
|
|
/* GUC variable: if false, prefer client ciphers */
|
|
bool SSLPreferServerCiphers;
|
|
|
|
/* ------------------------------------------------------------ */
|
|
/* Procedures common to all secure sessions */
|
|
/* ------------------------------------------------------------ */
|
|
|
|
/*
|
|
* Initialize global context.
|
|
*
|
|
* If isServerStart is true, report any errors as FATAL (so we don't return).
|
|
* Otherwise, log errors at LOG level and return -1 to indicate trouble,
|
|
* preserving the old SSL state if any. Returns 0 if OK.
|
|
*/
|
|
int
|
|
secure_initialize(bool isServerStart)
|
|
{
|
|
#ifdef USE_SSL
|
|
return be_tls_init(isServerStart);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Destroy global context, if any.
|
|
*/
|
|
void
|
|
secure_destroy(void)
|
|
{
|
|
#ifdef USE_SSL
|
|
be_tls_destroy();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Indicate if we have loaded the root CA store to verify certificates
|
|
*/
|
|
bool
|
|
secure_loaded_verify_locations(void)
|
|
{
|
|
#ifdef USE_SSL
|
|
return ssl_loaded_verify_locations;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Attempt to negotiate secure session.
|
|
*/
|
|
int
|
|
secure_open_server(Port *port)
|
|
{
|
|
int r = 0;
|
|
|
|
#ifdef USE_SSL
|
|
r = be_tls_open_server(port);
|
|
#endif
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Close secure session.
|
|
*/
|
|
void
|
|
secure_close(Port *port)
|
|
{
|
|
#ifdef USE_SSL
|
|
if (port->ssl_in_use)
|
|
be_tls_close(port);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Read data from a secure connection.
|
|
*/
|
|
ssize_t
|
|
secure_read(Port *port, void *ptr, size_t len)
|
|
{
|
|
ssize_t n;
|
|
int waitfor;
|
|
|
|
retry:
|
|
#ifdef USE_SSL
|
|
waitfor = 0;
|
|
if (port->ssl_in_use)
|
|
{
|
|
n = be_tls_read(port, ptr, len, &waitfor);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
n = secure_raw_read(port, ptr, len);
|
|
waitfor = WL_SOCKET_READABLE;
|
|
}
|
|
|
|
/* In blocking mode, wait until the socket is ready */
|
|
if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN))
|
|
{
|
|
WaitEvent event;
|
|
|
|
Assert(waitfor);
|
|
|
|
ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
|
|
|
|
WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
|
|
WAIT_EVENT_CLIENT_READ);
|
|
|
|
/*
|
|
* If the postmaster has died, it's not safe to continue running,
|
|
* because it is the postmaster's job to kill us if some other backend
|
|
* exists uncleanly. Moreover, we won't run very well in this state;
|
|
* helper processes like walwriter and the bgwriter will exit, so
|
|
* performance may be poor. Finally, if we don't exit, pg_ctl will be
|
|
* unable to restart the postmaster without manual intervention, so no
|
|
* new connections can be accepted. Exiting clears the deck for a
|
|
* postmaster restart.
|
|
*
|
|
* (Note that we only make this check when we would otherwise sleep on
|
|
* our latch. We might still continue running for a while if the
|
|
* postmaster is killed in mid-query, or even through multiple queries
|
|
* if we never have to wait for read. We don't want to burn too many
|
|
* cycles checking for this very rare condition, and this should cause
|
|
* us to exit quickly in most cases.)
|
|
*/
|
|
if (event.events & WL_POSTMASTER_DEATH)
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_ADMIN_SHUTDOWN),
|
|
errmsg("terminating connection due to unexpected postmaster exit")));
|
|
|
|
/* Handle interrupt. */
|
|
if (event.events & WL_LATCH_SET)
|
|
{
|
|
ResetLatch(MyLatch);
|
|
ProcessClientReadInterrupt(true);
|
|
|
|
/*
|
|
* We'll retry the read. Most likely it will return immediately
|
|
* because there's still no data available, and we'll wait for the
|
|
* socket to become ready again.
|
|
*/
|
|
}
|
|
goto retry;
|
|
}
|
|
|
|
/*
|
|
* Process interrupts that happened while (or before) receiving. Note that
|
|
* we signal that we're not blocking, which will prevent some types of
|
|
* interrupts from being processed.
|
|
*/
|
|
ProcessClientReadInterrupt(false);
|
|
|
|
return n;
|
|
}
|
|
|
|
ssize_t
|
|
secure_raw_read(Port *port, void *ptr, size_t len)
|
|
{
|
|
ssize_t n;
|
|
|
|
/*
|
|
* Try to read from the socket without blocking. If it succeeds we're
|
|
* done, otherwise we'll wait for the socket using the latch mechanism.
|
|
*/
|
|
#ifdef WIN32
|
|
pgwin32_noblock = true;
|
|
#endif
|
|
n = recv(port->sock, ptr, len, 0);
|
|
#ifdef WIN32
|
|
pgwin32_noblock = false;
|
|
#endif
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write data to a secure connection.
|
|
*/
|
|
ssize_t
|
|
secure_write(Port *port, void *ptr, size_t len)
|
|
{
|
|
ssize_t n;
|
|
int waitfor;
|
|
|
|
retry:
|
|
waitfor = 0;
|
|
#ifdef USE_SSL
|
|
if (port->ssl_in_use)
|
|
{
|
|
n = be_tls_write(port, ptr, len, &waitfor);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
n = secure_raw_write(port, ptr, len);
|
|
waitfor = WL_SOCKET_WRITEABLE;
|
|
}
|
|
|
|
if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN))
|
|
{
|
|
WaitEvent event;
|
|
|
|
Assert(waitfor);
|
|
|
|
ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
|
|
|
|
WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
|
|
WAIT_EVENT_CLIENT_WRITE);
|
|
|
|
/* See comments in secure_read. */
|
|
if (event.events & WL_POSTMASTER_DEATH)
|
|
ereport(FATAL,
|
|
(errcode(ERRCODE_ADMIN_SHUTDOWN),
|
|
errmsg("terminating connection due to unexpected postmaster exit")));
|
|
|
|
/* Handle interrupt. */
|
|
if (event.events & WL_LATCH_SET)
|
|
{
|
|
ResetLatch(MyLatch);
|
|
ProcessClientWriteInterrupt(true);
|
|
|
|
/*
|
|
* We'll retry the write. Most likely it will return immediately
|
|
* because there's still no data available, and we'll wait for the
|
|
* socket to become ready again.
|
|
*/
|
|
}
|
|
goto retry;
|
|
}
|
|
|
|
/*
|
|
* Process interrupts that happened while (or before) sending. Note that
|
|
* we signal that we're not blocking, which will prevent some types of
|
|
* interrupts from being processed.
|
|
*/
|
|
ProcessClientWriteInterrupt(false);
|
|
|
|
return n;
|
|
}
|
|
|
|
ssize_t
|
|
secure_raw_write(Port *port, const void *ptr, size_t len)
|
|
{
|
|
ssize_t n;
|
|
|
|
#ifdef WIN32
|
|
pgwin32_noblock = true;
|
|
#endif
|
|
n = send(port->sock, ptr, len, 0);
|
|
#ifdef WIN32
|
|
pgwin32_noblock = false;
|
|
#endif
|
|
|
|
return n;
|
|
}
|