1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-19 13:42:17 +03:00
Files
postgres/src/interfaces/libpq/fe-secure.c
Tom Lane 382ceffdf7 Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.

By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis.  However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent.  That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.

This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.

This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.

Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:35:54 -04:00

506 lines
11 KiB
C

/*-------------------------------------------------------------------------
*
* fe-secure.c
* functions related to setting up a secure connection to the backend.
* 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/interfaces/libpq/fe-secure.c
*
* NOTES
*
* We don't provide informational callbacks here (like
* info_cb() in be-secure.c), since there's no good mechanism to
* display such information to the user.
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include "libpq-fe.h"
#include "fe-auth.h"
#include "libpq-int.h"
#ifdef WIN32
#include "win32.h"
#else
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#include <arpa/inet.h>
#endif
#include <sys/stat.h>
#ifdef ENABLE_THREAD_SAFETY
#ifdef WIN32
#include "pthread-win32.h"
#else
#include <pthread.h>
#endif
#endif
/*
* Macros to handle disabling and then restoring the state of SIGPIPE handling.
* On Windows, these are all no-ops since there's no SIGPIPEs.
*/
#ifndef WIN32
#define SIGPIPE_MASKED(conn) ((conn)->sigpipe_so || (conn)->sigpipe_flag)
#ifdef ENABLE_THREAD_SAFETY
struct sigpipe_info
{
sigset_t oldsigmask;
bool sigpipe_pending;
bool got_epipe;
};
#define DECLARE_SIGPIPE_INFO(spinfo) struct sigpipe_info spinfo
#define DISABLE_SIGPIPE(conn, spinfo, failaction) \
do { \
(spinfo).got_epipe = false; \
if (!SIGPIPE_MASKED(conn)) \
{ \
if (pq_block_sigpipe(&(spinfo).oldsigmask, \
&(spinfo).sigpipe_pending) < 0) \
failaction; \
} \
} while (0)
#define REMEMBER_EPIPE(spinfo, cond) \
do { \
if (cond) \
(spinfo).got_epipe = true; \
} while (0)
#define RESTORE_SIGPIPE(conn, spinfo) \
do { \
if (!SIGPIPE_MASKED(conn)) \
pq_reset_sigpipe(&(spinfo).oldsigmask, (spinfo).sigpipe_pending, \
(spinfo).got_epipe); \
} while (0)
#else /* !ENABLE_THREAD_SAFETY */
#define DECLARE_SIGPIPE_INFO(spinfo) pqsigfunc spinfo = NULL
#define DISABLE_SIGPIPE(conn, spinfo, failaction) \
do { \
if (!SIGPIPE_MASKED(conn)) \
spinfo = pqsignal(SIGPIPE, SIG_IGN); \
} while (0)
#define REMEMBER_EPIPE(spinfo, cond)
#define RESTORE_SIGPIPE(conn, spinfo) \
do { \
if (!SIGPIPE_MASKED(conn)) \
pqsignal(SIGPIPE, spinfo); \
} while (0)
#endif /* ENABLE_THREAD_SAFETY */
#else /* WIN32 */
#define DECLARE_SIGPIPE_INFO(spinfo)
#define DISABLE_SIGPIPE(conn, spinfo, failaction)
#define REMEMBER_EPIPE(spinfo, cond)
#define RESTORE_SIGPIPE(conn, spinfo)
#endif /* WIN32 */
/* ------------------------------------------------------------ */
/* Procedures common to all secure sessions */
/* ------------------------------------------------------------ */
/*
* Exported function to allow application to tell us it's already
* initialized OpenSSL.
*/
void
PQinitSSL(int do_init)
{
#ifdef USE_SSL
pgtls_init_library(do_init, do_init);
#endif
}
/*
* Exported function to allow application to tell us it's already
* initialized OpenSSL and/or libcrypto.
*/
void
PQinitOpenSSL(int do_ssl, int do_crypto)
{
#ifdef USE_SSL
pgtls_init_library(do_ssl, do_crypto);
#endif
}
/*
* Initialize global SSL context
*/
int
pqsecure_initialize(PGconn *conn)
{
int r = 0;
#ifdef USE_SSL
r = pgtls_init(conn);
#endif
return r;
}
/*
* Begin or continue negotiating a secure session.
*/
PostgresPollingStatusType
pqsecure_open_client(PGconn *conn)
{
#ifdef USE_SSL
return pgtls_open_client(conn);
#else
/* shouldn't get here */
return PGRES_POLLING_FAILED;
#endif
}
/*
* Close secure session.
*/
void
pqsecure_close(PGconn *conn)
{
#ifdef USE_SSL
if (conn->ssl_in_use)
pgtls_close(conn);
#endif
}
/*
* Read data from a secure connection.
*
* On failure, this function is responsible for putting a suitable message
* into conn->errorMessage. The caller must still inspect errno, but only
* to determine whether to continue/retry after error.
*/
ssize_t
pqsecure_read(PGconn *conn, void *ptr, size_t len)
{
ssize_t n;
#ifdef USE_SSL
if (conn->ssl_in_use)
{
n = pgtls_read(conn, ptr, len);
}
else
#endif
{
n = pqsecure_raw_read(conn, ptr, len);
}
return n;
}
ssize_t
pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
{
ssize_t n;
int result_errno = 0;
char sebuf[256];
n = recv(conn->sock, ptr, len, 0);
if (n < 0)
{
result_errno = SOCK_ERRNO;
/* Set error message if appropriate */
switch (result_errno)
{
#ifdef EAGAIN
case EAGAIN:
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
case EWOULDBLOCK:
#endif
case EINTR:
/* no error message, caller is expected to retry */
break;
#ifdef ECONNRESET
case ECONNRESET:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext(
"server closed the connection unexpectedly\n"
"\tThis probably means the server terminated abnormally\n"
"\tbefore or while processing the request.\n"));
break;
#endif
default:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not receive data from server: %s\n"),
SOCK_STRERROR(result_errno,
sebuf, sizeof(sebuf)));
break;
}
}
/* ensure we return the intended errno to caller */
SOCK_ERRNO_SET(result_errno);
return n;
}
/*
* Write data to a secure connection.
*
* On failure, this function is responsible for putting a suitable message
* into conn->errorMessage. The caller must still inspect errno, but only
* to determine whether to continue/retry after error.
*/
ssize_t
pqsecure_write(PGconn *conn, const void *ptr, size_t len)
{
ssize_t n;
#ifdef USE_SSL
if (conn->ssl_in_use)
{
n = pgtls_write(conn, ptr, len);
}
else
#endif
{
n = pqsecure_raw_write(conn, ptr, len);
}
return n;
}
ssize_t
pqsecure_raw_write(PGconn *conn, const void *ptr, size_t len)
{
ssize_t n;
int flags = 0;
int result_errno = 0;
char sebuf[256];
DECLARE_SIGPIPE_INFO(spinfo);
#ifdef MSG_NOSIGNAL
if (conn->sigpipe_flag)
flags |= MSG_NOSIGNAL;
retry_masked:
#endif /* MSG_NOSIGNAL */
DISABLE_SIGPIPE(conn, spinfo, return -1);
n = send(conn->sock, ptr, len, flags);
if (n < 0)
{
result_errno = SOCK_ERRNO;
/*
* If we see an EINVAL, it may be because MSG_NOSIGNAL isn't available
* on this machine. So, clear sigpipe_flag so we don't try the flag
* again, and retry the send().
*/
#ifdef MSG_NOSIGNAL
if (flags != 0 && result_errno == EINVAL)
{
conn->sigpipe_flag = false;
flags = 0;
goto retry_masked;
}
#endif /* MSG_NOSIGNAL */
/* Set error message if appropriate */
switch (result_errno)
{
#ifdef EAGAIN
case EAGAIN:
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
case EWOULDBLOCK:
#endif
case EINTR:
/* no error message, caller is expected to retry */
break;
case EPIPE:
/* Set flag for EPIPE */
REMEMBER_EPIPE(spinfo, true);
/* FALL THRU */
#ifdef ECONNRESET
case ECONNRESET:
#endif
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext(
"server closed the connection unexpectedly\n"
"\tThis probably means the server terminated abnormally\n"
"\tbefore or while processing the request.\n"));
break;
default:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not send data to server: %s\n"),
SOCK_STRERROR(result_errno,
sebuf, sizeof(sebuf)));
break;
}
}
RESTORE_SIGPIPE(conn, spinfo);
/* ensure we return the intended errno to caller */
SOCK_ERRNO_SET(result_errno);
return n;
}
/* Dummy versions of SSL info functions, when built without SSL support */
#ifndef USE_SSL
int
PQsslInUse(PGconn *conn)
{
return 0;
}
void *
PQgetssl(PGconn *conn)
{
return NULL;
}
void *
PQsslStruct(PGconn *conn, const char *struct_name)
{
return NULL;
}
const char *
PQsslAttribute(PGconn *conn, const char *attribute_name)
{
return NULL;
}
const char *const *
PQsslAttributeNames(PGconn *conn)
{
static const char *const result[] = {NULL};
return result;
}
#endif /* USE_SSL */
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
/*
* Block SIGPIPE for this thread. This prevents send()/write() from exiting
* the application.
*/
int
pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
{
sigset_t sigpipe_sigset;
sigset_t sigset;
sigemptyset(&sigpipe_sigset);
sigaddset(&sigpipe_sigset, SIGPIPE);
/* Block SIGPIPE and save previous mask for later reset */
SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset));
if (SOCK_ERRNO)
return -1;
/* We can have a pending SIGPIPE only if it was blocked before */
if (sigismember(osigset, SIGPIPE))
{
/* Is there a pending SIGPIPE? */
if (sigpending(&sigset) != 0)
return -1;
if (sigismember(&sigset, SIGPIPE))
*sigpipe_pending = true;
else
*sigpipe_pending = false;
}
else
*sigpipe_pending = false;
return 0;
}
/*
* Discard any pending SIGPIPE and reset the signal mask.
*
* Note: we are effectively assuming here that the C library doesn't queue
* up multiple SIGPIPE events. If it did, then we'd accidentally leave
* ours in the queue when an event was already pending and we got another.
* As long as it doesn't queue multiple events, we're OK because the caller
* can't tell the difference.
*
* The caller should say got_epipe = FALSE if it is certain that it
* didn't get an EPIPE error; in that case we'll skip the clear operation
* and things are definitely OK, queuing or no. If it got one or might have
* gotten one, pass got_epipe = TRUE.
*
* We do not want this to change errno, since if it did that could lose
* the error code from a preceding send(). We essentially assume that if
* we were able to do pq_block_sigpipe(), this can't fail.
*/
void
pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
{
int save_errno = SOCK_ERRNO;
int signo;
sigset_t sigset;
/* Clear SIGPIPE only if none was pending */
if (got_epipe && !sigpipe_pending)
{
if (sigpending(&sigset) == 0 &&
sigismember(&sigset, SIGPIPE))
{
sigset_t sigpipe_sigset;
sigemptyset(&sigpipe_sigset);
sigaddset(&sigpipe_sigset, SIGPIPE);
sigwait(&sigpipe_sigset, &signo);
}
}
/* Restore saved block mask */
pthread_sigmask(SIG_SETMASK, osigset, NULL);
SOCK_ERRNO_SET(save_errno);
}
#endif /* ENABLE_THREAD_SAFETY && !WIN32 */