summaryrefslogtreecommitdiffstats
path: root/src/backend/libpq
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:18:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:18:03 +0000
commitb4b8efbd3826ac0af2d1c2e7c40fcf80a4bfba45 (patch)
treebec866278030c41c624a91037b1dd88f41c99d8e /src/backend/libpq
parentAdding upstream version 15.5. (diff)
downloadpostgresql-15-upstream/15.6.tar.xz
postgresql-15-upstream/15.6.zip
Adding upstream version 15.6.upstream/15.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/libpq')
-rw-r--r--src/backend/libpq/auth.c12
-rw-r--r--src/backend/libpq/be-secure-gssapi.c60
-rw-r--r--src/backend/libpq/be-secure-openssl.c26
-rw-r--r--src/backend/libpq/pqcomm.c22
4 files changed, 65 insertions, 55 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index efc53f3..235152e 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -850,15 +850,13 @@ CheckPWChallengeAuth(Port *port, const char **logdetail)
if (shadow_pass)
pfree(shadow_pass);
-
- /*
- * If get_role_password() returned error, return error, even if the
- * authentication succeeded.
- */
- if (!shadow_pass)
+ else
{
+ /*
+ * If get_role_password() returned error, authentication better not
+ * have succeeded.
+ */
Assert(auth_result != STATUS_OK);
- return STATUS_ERROR;
}
if (auth_result == STATUS_OK)
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 2844c5a..d3337a3 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -60,8 +60,8 @@ static char *PqGSSSendBuffer; /* Encrypted data waiting to be sent */
static int PqGSSSendLength; /* End of data available in PqGSSSendBuffer */
static int PqGSSSendNext; /* Next index to send a byte from
* PqGSSSendBuffer */
-static int PqGSSSendConsumed; /* Number of *unencrypted* bytes consumed for
- * current contents of PqGSSSendBuffer */
+static int PqGSSSendConsumed; /* Number of source bytes encrypted but not
+ * yet reported as sent */
static char *PqGSSRecvBuffer; /* Received, encrypted data */
static int PqGSSRecvLength; /* End of data available in PqGSSRecvBuffer */
@@ -83,8 +83,8 @@ static uint32 PqGSSMaxPktSize; /* Maximum size we can encrypt and fit the
*
* On success, returns the number of data bytes consumed (possibly less than
* len). On failure, returns -1 with errno set appropriately. For retryable
- * errors, caller should call again (passing the same data) once the socket
- * is ready.
+ * errors, caller should call again (passing the same or more data) once the
+ * socket is ready.
*
* Dealing with fatal errors here is a bit tricky: we can't invoke elog(FATAL)
* since it would try to write to the client, probably resulting in infinite
@@ -98,19 +98,25 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
minor;
gss_buffer_desc input,
output;
- size_t bytes_sent = 0;
size_t bytes_to_encrypt;
size_t bytes_encrypted;
gss_ctx_id_t gctx = port->gss->ctx;
/*
- * When we get a failure, we must not tell the caller we have successfully
- * transmitted everything, else it won't retry. Hence a "success"
- * (positive) return value must only count source bytes corresponding to
- * fully-transmitted encrypted packets. The amount of source data
- * corresponding to the current partly-transmitted packet is remembered in
+ * When we get a retryable failure, we must not tell the caller we have
+ * successfully transmitted everything, else it won't retry. For
+ * simplicity, we claim we haven't transmitted anything until we have
+ * successfully transmitted all "len" bytes. Between calls, the amount of
+ * the current input data that's already been encrypted and placed into
+ * PqGSSSendBuffer (and perhaps transmitted) is remembered in
* PqGSSSendConsumed. On a retry, the caller *must* be sending that data
* again, so if it offers a len less than that, something is wrong.
+ *
+ * Note: it may seem attractive to report partial write completion once
+ * we've successfully sent any encrypted packets. However, that can cause
+ * problems for callers; notably, pqPutMsgEnd's heuristic to send only
+ * full 8K blocks interacts badly with such a hack. We won't save much,
+ * typically, by letting callers discard data early, so don't risk it.
*/
if (len < PqGSSSendConsumed)
{
@@ -118,6 +124,7 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
errno = ECONNRESET;
return -1;
}
+
/* Discount whatever source data we already encrypted. */
bytes_to_encrypt = len - PqGSSSendConsumed;
bytes_encrypted = PqGSSSendConsumed;
@@ -146,33 +153,20 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
ret = secure_raw_write(port, PqGSSSendBuffer + PqGSSSendNext, amount);
if (ret <= 0)
- {
- /*
- * Report any previously-sent data; if there was none, reflect
- * the secure_raw_write result up to our caller. When there
- * was some, we're effectively assuming that any interesting
- * failure condition will recur on the next try.
- */
- if (bytes_sent)
- return bytes_sent;
return ret;
- }
/*
* Check if this was a partial write, and if so, move forward that
* far in our buffer and try again.
*/
- if (ret != amount)
+ if (ret < amount)
{
PqGSSSendNext += ret;
continue;
}
- /* We've successfully sent whatever data was in that packet. */
- bytes_sent += PqGSSSendConsumed;
-
- /* All encrypted data was sent, our buffer is empty now. */
- PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
+ /* We've successfully sent whatever data was in the buffer. */
+ PqGSSSendLength = PqGSSSendNext = 0;
}
/*
@@ -196,7 +190,10 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
output.value = NULL;
output.length = 0;
- /* Create the next encrypted packet */
+ /*
+ * Create the next encrypted packet. Any failure here is considered a
+ * hard failure, so we return -1 even if some data has been sent.
+ */
major = gss_wrap(&minor, gctx, 1, GSS_C_QOP_DEFAULT,
&input, &conf_state, &output);
if (major != GSS_S_COMPLETE)
@@ -239,10 +236,13 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
}
/* If we get here, our counters should all match up. */
- Assert(bytes_sent == len);
- Assert(bytes_sent == bytes_encrypted);
+ Assert(len == PqGSSSendConsumed);
+ Assert(len == bytes_encrypted);
+
+ /* We're reporting all the data as sent, so reset PqGSSSendConsumed. */
+ PqGSSSendConsumed = 0;
- return bytes_sent;
+ return bytes_encrypted;
}
/*
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index f5c5ed2..ed13e8b 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -457,6 +457,7 @@ aloop:
* per-thread error queue following another call to an OpenSSL I/O
* routine.
*/
+ errno = 0;
ERR_clear_error();
r = SSL_accept(port->ssl);
if (r <= 0)
@@ -493,7 +494,7 @@ aloop:
WAIT_EVENT_SSL_OPEN_SERVER);
goto aloop;
case SSL_ERROR_SYSCALL:
- if (r < 0)
+ if (r < 0 && errno != 0)
ereport(COMMERROR,
(errcode_for_socket_access(),
errmsg("could not accept SSL connection: %m")));
@@ -727,7 +728,7 @@ be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
break;
case SSL_ERROR_SYSCALL:
/* leave it to caller to ereport the value of errno */
- if (n != -1)
+ if (n != -1 || errno == 0)
{
errno = ECONNRESET;
n = -1;
@@ -785,8 +786,14 @@ be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
n = -1;
break;
case SSL_ERROR_SYSCALL:
- /* leave it to caller to ereport the value of errno */
- if (n != -1)
+
+ /*
+ * Leave it to caller to ereport the value of errno. However, if
+ * errno is still zero then assume it's a read EOF situation, and
+ * report ECONNRESET. (This seems possible because SSL_write can
+ * also do reads.)
+ */
+ if (n != -1 || errno == 0)
{
errno = ECONNRESET;
n = -1;
@@ -839,11 +846,6 @@ be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
* to retry; do we need to adopt their logic for that?
*/
-#ifndef HAVE_BIO_GET_DATA
-#define BIO_get_data(bio) (bio->ptr)
-#define BIO_set_data(bio, data) (bio->ptr = data)
-#endif
-
static BIO_METHOD *my_bio_methods = NULL;
static int
@@ -853,7 +855,7 @@ my_sock_read(BIO *h, char *buf, int size)
if (buf != NULL)
{
- res = secure_raw_read(((Port *) BIO_get_data(h)), buf, size);
+ res = secure_raw_read(((Port *) BIO_get_app_data(h)), buf, size);
BIO_clear_retry_flags(h);
if (res <= 0)
{
@@ -873,7 +875,7 @@ my_sock_write(BIO *h, const char *buf, int size)
{
int res = 0;
- res = secure_raw_write(((Port *) BIO_get_data(h)), buf, size);
+ res = secure_raw_write(((Port *) BIO_get_app_data(h)), buf, size);
BIO_clear_retry_flags(h);
if (res <= 0)
{
@@ -949,7 +951,7 @@ my_SSL_set_fd(Port *port, int fd)
SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
goto err;
}
- BIO_set_data(bio, port);
+ BIO_set_app_data(bio, port);
BIO_set_fd(bio, fd, BIO_NOCLOSE);
SSL_set_bio(port->ssl, bio, bio);
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 75392a8..bb9fa77 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -954,6 +954,8 @@ pq_recvbuf(void)
{
int r;
+ errno = 0;
+
r = secure_read(MyProcPort, PqRecvBuffer + PqRecvLength,
PQ_RECV_BUFFER_SIZE - PqRecvLength);
@@ -966,10 +968,13 @@ pq_recvbuf(void)
* Careful: an ereport() that tries to write to the client would
* cause recursion to here, leading to stack overflow and core
* dump! This message must go *only* to the postmaster log.
+ *
+ * If errno is zero, assume it's EOF and let the caller complain.
*/
- ereport(COMMERROR,
- (errcode_for_socket_access(),
- errmsg("could not receive data from client: %m")));
+ if (errno != 0)
+ ereport(COMMERROR,
+ (errcode_for_socket_access(),
+ errmsg("could not receive data from client: %m")));
return EOF;
}
if (r == 0)
@@ -1046,6 +1051,8 @@ pq_getbyte_if_available(unsigned char *c)
/* Put the socket into non-blocking mode */
socket_set_nonblocking(true);
+ errno = 0;
+
r = secure_read(MyProcPort, c, 1);
if (r < 0)
{
@@ -1062,10 +1069,13 @@ pq_getbyte_if_available(unsigned char *c)
* Careful: an ereport() that tries to write to the client would
* cause recursion to here, leading to stack overflow and core
* dump! This message must go *only* to the postmaster log.
+ *
+ * If errno is zero, assume it's EOF and let the caller complain.
*/
- ereport(COMMERROR,
- (errcode_for_socket_access(),
- errmsg("could not receive data from client: %m")));
+ if (errno != 0)
+ ereport(COMMERROR,
+ (errcode_for_socket_access(),
+ errmsg("could not receive data from client: %m")));
r = EOF;
}
}