aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-secure-gssapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/libpq/fe-secure-gssapi.c')
-rw-r--r--src/interfaces/libpq/fe-secure-gssapi.c68
1 files changed, 50 insertions, 18 deletions
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index ce183bc04b4..62d05f68496 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -47,11 +47,18 @@
* don't want the other side to send arbitrarily huge packets as we
* would have to allocate memory for them to then pass them to GSSAPI.
*
- * Therefore, these two #define's are effectively part of the protocol
+ * Therefore, this #define is effectively part of the protocol
* spec and can't ever be changed.
*/
-#define PQ_GSS_SEND_BUFFER_SIZE 16384
-#define PQ_GSS_RECV_BUFFER_SIZE 16384
+#define PQ_GSS_MAX_PACKET_SIZE 16384 /* includes uint32 header word */
+
+/*
+ * However, during the authentication exchange we must cope with whatever
+ * message size the GSSAPI library wants to send (because our protocol
+ * doesn't support splitting those messages). Depending on configuration
+ * those messages might be as much as 64kB.
+ */
+#define PQ_GSS_AUTH_BUFFER_SIZE 65536 /* includes uint32 header word */
/*
* We need these state variables per-connection. To allow the functions
@@ -203,11 +210,11 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
goto cleanup;
}
- if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+ if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
{
libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
(size_t) output.length,
- PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32));
+ PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
errno = EIO; /* for lack of a better idea */
goto cleanup;
}
@@ -342,11 +349,11 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)
/* Decode the packet length and check for overlength packet */
input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
- if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+ if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
{
libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
(size_t) input.length,
- PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
+ PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
errno = EIO; /* for lack of a better idea */
return -1;
}
@@ -485,12 +492,15 @@ pqsecure_open_gss(PGconn *conn)
* initialize state variables. By malloc'ing the buffers separately, we
* ensure that they are sufficiently aligned for the length-word accesses
* that we do in some places in this file.
+ *
+ * We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
+ * negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
*/
if (PqGSSSendBuffer == NULL)
{
- PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
- PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
- PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
+ PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+ PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+ PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
{
libpq_append_conn_error(conn, "out of memory");
@@ -564,13 +574,13 @@ pqsecure_open_gss(PGconn *conn)
* so leave a spot at the end for a NULL byte too) and report that
* back to the caller.
*/
- result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_RECV_BUFFER_SIZE - PqGSSRecvLength - 1, &ret);
+ result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_AUTH_BUFFER_SIZE - PqGSSRecvLength - 1, &ret);
if (result != PGRES_POLLING_OK)
return result;
PqGSSRecvLength += ret;
- Assert(PqGSSRecvLength < PQ_GSS_RECV_BUFFER_SIZE);
+ Assert(PqGSSRecvLength < PQ_GSS_AUTH_BUFFER_SIZE);
PqGSSRecvBuffer[PqGSSRecvLength] = '\0';
appendPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);
@@ -584,11 +594,11 @@ pqsecure_open_gss(PGconn *conn)
/* Get the length and check for over-length packet */
input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
- if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+ if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
{
libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
(size_t) input.length,
- PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
+ PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
return PGRES_POLLING_FAILED;
}
@@ -669,11 +679,32 @@ pqsecure_open_gss(PGconn *conn)
gss_release_buffer(&minor, &output);
/*
+ * Release the large authentication buffers and allocate the ones we
+ * want for normal operation. (This maneuver is safe only because
+ * pqDropConnection will drop the buffers; otherwise, during a
+ * reconnection we'd be at risk of using undersized buffers during
+ * negotiation.)
+ */
+ free(PqGSSSendBuffer);
+ free(PqGSSRecvBuffer);
+ free(PqGSSResultBuffer);
+ PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+ PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+ PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+ if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
+ {
+ libpq_append_conn_error(conn, "out of memory");
+ return PGRES_POLLING_FAILED;
+ }
+ PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
+ PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
+
+ /*
* Determine the max packet size which will fit in our buffer, after
* accounting for the length. pg_GSS_write will need this.
*/
major = gss_wrap_size_limit(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
- PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
+ PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
&PqGSSMaxPktSize);
if (GSS_ERROR(major))
@@ -687,10 +718,11 @@ pqsecure_open_gss(PGconn *conn)
}
/* Must have output.length > 0 */
- if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+ if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
{
- pg_GSS_error(libpq_gettext("GSSAPI context establishment error"),
- conn, major, minor);
+ libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
+ (size_t) output.length,
+ PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
gss_release_buffer(&minor, &output);
return PGRES_POLLING_FAILED;
}