Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/interfaces/libpq/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 13 kB image not shown  

Quelle  fe-secure.c

  Sprache: 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-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *   src/interfaces/libpq/fe-secure.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres_fe.h"

#include <signal.h>
#include <fcntl.h>
#include <ctype.h>

#ifdef WIN32
#include "win32.h"
#else
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#endif

#include <sys/stat.h>

#ifdef WIN32
#include "pthread-win32.h"
#else
#include <pthread.h>
#endif

#include "fe-auth.h"
#include "libpq-fe.h"
#include "libpq-int.h"

/*
 * 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)

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       /* 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 */
/* ------------------------------------------------------------ */


int
PQsslInUse(PGconn *conn)
{
 if (!conn)
  return 0;
 return conn->ssl_in_use;
}

/*
 * Exported function to allow application to tell us it's already initialized
 * OpenSSL.  Since OpenSSL 1.1.0 it is no longer required to explicitly
 * initialize libssl and libcrypto, so this is a no-op.  This function remains
 * for backwards API compatibility.
 */

void
PQinitSSL(int do_init)
{
 /* no-op */
}

/*
 * Exported function to allow application to tell us it's already initialized
 * OpenSSL.  Since OpenSSL 1.1.0 it is no longer required to explicitly
 * initialize libssl and libcrypto, so this is a no-op.  This function remains
 * for backwards API compatibility.
 */

void
PQinitOpenSSL(int do_ssl, int do_crypto)
{
 /* no-op */
}

/*
 * 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
 pgtls_close(conn);
#endif
}

/*
 * Read data from a secure connection.
 *
 * On failure, this function is responsible for appending a suitable message
 * to 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
#ifdef ENABLE_GSS
 if (conn->gssenc)
 {
  n = pg_GSS_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[PG_STRERROR_R_BUFLEN];

 SOCK_ERRNO_SET(0);

 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;

   case EPIPE:
   case ECONNRESET:
    libpq_append_conn_error(conn, "server closed the connection unexpectedly\n"
          "\tThis probably means the server terminated abnormally\n"
          "\tbefore or while processing the request.");
    break;

   case 0:
    /* If errno didn't get set, treat it as regular EOF */
    n = 0;
    break;

   default:
    libpq_append_conn_error(conn, "could not receive data from server: %s",
          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.
 *
 * Returns the number of bytes written, or a negative value (with errno
 * set) upon failure.  The write count could be less than requested.
 *
 * Note that socket-level hard failures are masked from the caller,
 * instead setting conn->write_failed and storing an error message
 * in conn->write_err_msg; see pqsecure_raw_write.  This allows us to
 * postpone reporting of write failures until we're sure no error
 * message is available from the server.
 *
 * However, errors detected in the SSL or GSS management level are reported
 * via a negative result, with message appended to conn->errorMessage.
 * It's frequently unclear whether such errors should be considered read or
 * write errors, so we don't attempt to postpone reporting them.
 *
 * The caller must still inspect errno upon failure, but only to determine
 * whether to continue/retry; a message has been saved someplace in any case.
 */

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
#ifdef ENABLE_GSS
 if (conn->gssenc)
 {
  n = pg_GSS_write(conn, ptr, len);
 }
 else
#endif
 {
  n = pqsecure_raw_write(conn, ptr, len);
 }

 return n;
}

/*
 * Low-level implementation of pqsecure_write.
 *
 * This is used directly for an unencrypted connection.  For encrypted
 * connections, this does the physical I/O on behalf of pgtls_write or
 * pg_GSS_write.
 *
 * This function reports failure (i.e., returns a negative result) only
 * for retryable errors such as EINTR.  Looping for such cases is to be
 * handled at some outer level, maybe all the way up to the application.
 * For hard failures, we set conn->write_failed and store an error message
 * in conn->write_err_msg, but then claim to have written the data anyway.
 * This is because we don't want to report write failures so long as there
 * is a possibility of reading from the server and getting an error message
 * that could explain why the connection dropped.  Many TCP stacks have
 * race conditions such that a write failure may or may not be reported
 * before all incoming data has been read.
 *
 * Note that this error behavior happens below the SSL management level when
 * we are using SSL.  That's because at least some versions of OpenSSL are
 * too quick to report a write failure when there's still a possibility to
 * get a more useful error from the server.
 */

ssize_t
pqsecure_raw_write(PGconn *conn, const void *ptr, size_t len)
{
 ssize_t  n;
 int   flags = 0;
 int   result_errno = 0;
 char  msgbuf[1024];
 char  sebuf[PG_STRERROR_R_BUFLEN];

 DECLARE_SIGPIPE_INFO(spinfo);

 /*
  * If we already had a write failure, we will never again try to send data
  * on that connection.  Even if the kernel would let us, we've probably
  * lost message boundary sync with the server.  conn->write_failed
  * therefore persists until the connection is reset, and we just discard
  * all data presented to be written.
 */

 if (conn->write_failed)
  return len;

#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 */

   case ECONNRESET:
    conn->write_failed = true;
    /* Store error message in conn->write_err_msg, if possible */
    /* (strdup failure is OK, we'll cope later) */
    snprintf(msgbuf, sizeof(msgbuf),
       libpq_gettext("server closed the connection unexpectedly\n"
            "\tThis probably means the server terminated abnormally\n"
            "\tbefore or while processing the request."));
    /* keep newline out of translated string */
    strlcat(msgbuf, "\n"sizeof(msgbuf));
    conn->write_err_msg = strdup(msgbuf);
    /* Now claim the write succeeded */
    n = len;
    break;

   default:
    conn->write_failed = true;
    /* Store error message in conn->write_err_msg, if possible */
    /* (strdup failure is OK, we'll cope later) */
    snprintf(msgbuf, sizeof(msgbuf),
       libpq_gettext("could not send data to server: %s"),
       SOCK_STRERROR(result_errno,
            sebuf, sizeof(sebuf)));
    /* keep newline out of translated string */
    strlcat(msgbuf, "\n"sizeof(msgbuf));
    conn->write_err_msg = strdup(msgbuf);
    /* Now claim the write succeeded */
    n = len;
    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

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 */

/*
 * Dummy versions of OpenSSL key password hook functions, when built without
 * OpenSSL.
 */

#ifndef USE_OPENSSL

PQsslKeyPassHook_OpenSSL_type
PQgetSSLKeyPassHook_OpenSSL(void)
{
 return NULL;
}

void
PQsetSSLKeyPassHook_OpenSSL(PQsslKeyPassHook_OpenSSL_type hook)
{
 return;
}

int
PQdefaultSSLKeyPassHook_OpenSSL(char *buf, int size, PGconn *conn)
{
 return 0;
}
#endif       /* USE_OPENSSL */

/* Dummy version of GSSAPI information functions, when built without GSS support */
#ifndef ENABLE_GSS

void *
PQgetgssctx(PGconn *conn)
{
 return NULL;
}

int
PQgssEncInUse(PGconn *conn)
{
 return 0;
}

#endif       /* ENABLE_GSS */


#if !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       /* !WIN32 */

Messung V0.5 in Prozent
C=93 H=95 G=93

¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.